【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=2726
【题意】
将n个任务划分成若干个块,每一组Mi任务花费代价(T+sigma{ tj }+s)*sima{ fi },j属于Mi,T为当前时间,问最小代价。
【思路】
设f[i]为将前i个任务划分完成的最小费用,Ti Fi分别表示t和f的前缀和,则不难写出转移方程式:
f[i]=min{ f[j]+(F[n]-F[j])*(T[i]-T[j]+s) },1<=j<=i-1
经过整理得到:
f[i]=min{ -T[i]*F[j]+(f[j]-F[n]*T[j]+F[j]*T[j]-s*F[j]) }
设X(i)=F[i],Y(i)=f[j]-F[n]*T[j]+F[j]*T[j]-s*F[j],a(i)=T[i]则有:
f[i]=min{ -a(i)*X(j)+Y(j) }
则我们需要:
min p = -a(i)*X(j)+Y(j)
即最小化直线方程:Y(j)=a(i)*X(j)+p的截距。
如果时间没有负数的话(出题人神脑洞<_<,这个题可以直接上单调队列维护下凸线。事实上这个算法可以得到60分。
有了负数以后因为斜率不满足随x单调,所以不能使用单调队列。
然后这道题就需要用splay或CDQ分治解。
Splay写法还是不会=-=,CDQ分治的处理和 这道题 类似,就不再赘述了。
需要注意的:x,y可能到达long long级别,所以不要直接除得slop
运算过程中long long的标识。
归并数组的时候几个小条件。
【代码】
1 #include<set> 2 #include<cmath> 3 #include<queue> 4 #include<vector> 5 #include<cstdio> 6 #include<cstring> 7 #include<iostream> 8 #include<algorithm> 9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt) 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 11 #define rep(a,b,c) for(int a=(b);a>=(c);a--) 12 using namespace std; 13 14 typedef long long ll; 15 const int N = 5e5+10; 16 const ll inf = 1e18; 17 18 ll read() { 19 char c=getchar(); 20 ll f=1,x=0; 21 while(!isdigit(c)) { 22 if(c=='-') f=-1; c=getchar(); 23 } 24 while(isdigit(c)) 25 x=x*10+c-'0',c=getchar(); 26 return x*f; 27 } 28 29 struct Pt 30 { 31 ll x,y,k; 32 int id; 33 bool operator < (const Pt& rhs) const 34 { 35 return k<rhs.k; 36 } 37 }q[N],t[N]; 38 39 bool cmp(const Pt& a,const Pt& b) 40 { 41 return a.x<b.x || a.x==b.x&&a.y<b.y; 42 } 43 44 int T[N],F[N],st[N],top,n,s; ll f[N]; 45 46 ll U(int j,int k) 47 { 48 return (ll)q[k].y-q[j].y; 49 } 50 ll D(int j,int k) 51 { 52 return (ll)q[k].x-q[j].x; 53 } 54 55 void solve(int l,int r) 56 { 57 if(l==r) { 58 q[l].x=(ll)F[l]; 59 q[l].y=(ll)f[l]-(ll)F[n]*T[l]+(ll)F[l]*T[l]-(ll)s*F[l]; 60 return ; 61 } 62 int mid=l+r>>1; 63 int l1=l,l2=mid+1; 64 FOR(i,l,r) { 65 if(q[i].id>mid) t[l2++]=q[i]; 66 else t[l1++]=q[i]; 67 } 68 memcpy(q+l,t+l,sizeof(Pt)*(r-l+1)); 69 solve(l,mid); 70 top=0; 71 FOR(i,l,mid) { 72 while(top>1 && (ll)U(st[top-1],st[top])*D(st[top-1],i)>(ll)U(st[top-1],i)*D(st[top-1],st[top])) top--; 73 st[++top]=i; 74 } 75 int j=1; 76 FOR(i,mid+1,r) { 77 while(j<top && U(st[j],st[j+1])<(ll)q[i].k*D(st[j],st[j+1])) j++; 78 f[q[i].id]=min(f[q[i].id],-(ll)q[i].k*q[st[j]].x+q[st[j]].y+(ll)F[n]*(T[q[i].id]+s)); 79 } 80 solve(mid+1,r); 81 l1=l,l2=mid+1; 82 FOR(i,l,r) { 83 if((l2>r||cmp(q[l1],q[l2]))&&l1<=mid) t[i]=q[l1++]; 84 else t[i]=q[l2++]; 85 } 86 memcpy(q+l,t+l,sizeof(Pt)*(r-l+1)); 87 } 88 89 int main() 90 { 91 // freopen("in.in","r",stdin); 92 // freopen("out.out","w",stdout); 93 n=read(),s=read(); 94 FOR(i,1,n) 95 T[i]=read(),T[i]+=T[i-1], 96 F[i]=read(),F[i]+=F[i-1]; 97 FOR(i,1,n) { 98 q[i].id=i; 99 q[i].k=T[i]; 100 f[i]=inf; 101 } 102 sort(q+1,q+n+1); 103 solve(0,n); 104 printf("%lld ",f[n]); 105 return 0; 106 }
简单DP(20分)
1 #include<set> 2 #include<cmath> 3 #include<queue> 4 #include<vector> 5 #include<cstdio> 6 #include<cstring> 7 #include<iostream> 8 #include<algorithm> 9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt) 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 11 #define rep(a,b,c) for(int a=(b);a>=(c);a--) 12 using namespace std; 13 14 typedef long long ll; 15 const int N = 2e5+10; 16 const ll inf = 1e18+10; 17 18 ll read() { 19 char c=getchar(); 20 ll f=1,x=0; 21 while(!isdigit(c)) { 22 if(c=='-') f=-1; c=getchar(); 23 } 24 while(isdigit(c)) 25 x=x*10+c-'0',c=getchar(); 26 return x*f; 27 } 28 29 ll T[N],F[N],f[N],pre[N],n,s; 30 31 int main() 32 { 33 freopen("in.in","r",stdin); 34 freopen("outr.out","w",stdout); 35 n=read(),s=read(); 36 FOR(i,1,n) 37 T[i]=read(),T[i]+=T[i-1],F[i]=read(),F[i]+=F[i-1] ; 38 FOR(i,1,n) { 39 f[i]=inf; 40 FOR(j,0,i-1) { 41 //f[i]=min(f[i],f[j]+(F[n]-F[j])*(T[i]-T[j]+s)); 42 int tmp=-T[i]*F[j]+(f[j]-F[n]*T[j]+F[j]*T[j]-s*F[j]); 43 if(tmp<f[i]) pre[i]=j,f[i]=tmp; 44 } 45 f[i]+=F[n]*(T[i]+s); 46 } 47 printf("%lld ",f[n]); 48 //FOR(i,1,n) { 49 // printf("%d %d ",F[i],f[i]-F[n]*T[i]+F[i]*T[i]-s*F[i]); 50 //} 51 return 0; 52 }