bzoj 2726: [SDOI2012]任务安排
Description
Input
Output
Sample Input
1 3
3 2
4 3
2 3
1 4
Sample Output
HINT
Source
这个题一开始想着去处理一个人等了多长时间
然后就会发现等的时间是和前面分了多少批有关的,这样的话我们需要用二维状态表示到这个点,前面分了多少批
这样暴力n^3会很萎
这时候我们会回想起一个叫做修车的题目,他对于每个点的处理相当于是这个点让后面的人多等了多久
那我们可以通过同样的方式思考,每分了一批其实就是让后面的所有人多等了一个S的时间,其余的并不影响
那么我们可以推出一维的状态
f[i]=min(f[j]+s*(F[n]-F[j])+T[i]*(F[i]-F[j]));
对于前百分之60的数据,T为正数,满足决策单调性,可以二分栈,但我懒得打了,就在codevs上AC了一个弱化版的n^2;
斜率优化+CDQ在下面
// MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<queue> #include<set> #include<cstdlib> #include<cstring> #include<string> #include<ctime> #define lson num<<1 #define rson num<<1|1 #define int long long using namespace std; typedef long long ll; const int N=100050; int gi() { int x=0,flag=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*flag; } int f[N],t[N],F[N],s,n; int cal(int j,int i){ return f[j]+s*(F[n]-F[j])+t[i]*(F[i]-F[j]); } main() { n=gi(),s=gi(); for(int i=1;i<=n;i++) t[i]=gi()+t[i-1],F[i]=gi()+F[i-1]; for(int i=1;i<=n;i++){ f[i]=cal(0,i); for(int j=1;j<i;j++){ f[i]=min(f[i],cal(j,i)); } } printf("%lld",f[n]); }
我们来推一推斜率方程:
f[j]+s*(F[n]-F[j])+T[i]*(F[i]-F[j)<=f[k]+s*(F[n]-F[k])+T[i]*(F[i]-F[k]);
f[j]+s*F[n]-s*F[j]+T[i]*F[i]-T[i]*F[j]<=f[k]+s*F[n]-s*F[k]+T[i]*F[i]-T[i]*F[k];
f[j]-s*F[j]-T[i]*F[j]<=f[k]-s*F[k]-T[i]*F[k];
令Y(j)=f[j]-s*F[j];
令X(j)=F[j];
则变为:
Y(j)-T[i]*X(j)<=Y(k)-T[i]*X(k);
T[i]*(X(k)-X(j))<=Y(k)-Y(j);
若X(k)>=X(j):T[i]<=(Y(k)-Y(j))/(X(k)-X(j));
若X(k)<=X(j);T[i]>=(Y(k)-Y(j))/(X(k)-X(j));
因为F值都是正的,所以X(k)>=X(j);横坐标单调;
所以T[i]<=(Y(k)-Y(j))/(X(k)-X(j));
然而斜率T[i]不是单调的
然后我就不会做了
上面的点的做法就是在瞎哔哔;
下面介绍线的神教:
方程:f[j]+s*(F[n]-F[j])+T[i]*(F[i]-F[j])
展开:
f[j]+s*F[n]-s*F[j]+T[i]*F[i]-T[i]*F[j]
整理:
f[j]-s*F[j]+s*F[n]+T[i]*F[i]*T[i]-F[j]*T[i]
利用线的套路转变:
b=f[j]-s*F[j];k=F[j];x=T[i],s*F[n]+F[i]*T[i]为常数
然后我们发现斜率k是满足单调性的,而横坐标x根据题意有负数是不满足单调性的。
这时候我们可以直接应用货币兑换的CDQ分治(把斜率和横坐标都不当成单调的搞):
在左边维护斜率单调构造半平面交
在右边维护横坐标单调进行决策转移
具体分析请参考货币兑换的博客,上面有详解
附上代码:
// MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<queue> #include<set> #include<cstdlib> #include<cstring> #include<string> #include<ctime> #define lson num<<1 #define rson num<<1|1 #define int long long using namespace std; typedef long long ll; const int N=300050; int gi() { int x=0,flag=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*flag; } struct data {int k,x,b;int id;}g[N],q[N],p[N]; bool cmp(data a,data b){ return a.x<b.x; } int f[N],F[N],T[N],s,n; void solve(int l,int r){ if(l==r) return; int mid=(l+r)>>1,l1=l,l2=mid+1,head=1,tail=0; for(int i=l;i<=r;i++){ if(g[i].id<=mid) p[l1++]=g[i]; else p[l2++]=g[i]; } for(int i=l;i<=r;i++) g[i]=p[i]; solve(l,mid); for(int i=l;i<=mid;i++){ while(head<tail&&(g[i].k-q[tail-1].k)*(q[tail-1].b-q[tail].b)>=(q[tail].k-q[tail-1].k)*(q[tail-1].b-g[i].b)) tail--; q[++tail]=g[i]; } //sort(g+mid+1,g+r+1,cmp); for(int i=mid+1;i<=r;i++){ while(head<tail&&q[head].k*g[i].x+q[head].b>=q[head+1].k*g[i].x+q[head+1].b) head++; f[g[i].id]=min(f[g[i].id],q[head].k*g[i].x+q[head].b+T[g[i].id]*F[g[i].id]+s*F[n]); g[i].b=f[g[i].id]-s*F[g[i].id]; } solve(mid+1,r);l1=l,l2=mid+1; for(int i=l;i<=r;i++){ if(l2>r||(l1<=mid&&g[l1].k>=g[l2].k)) p[i]=g[l1++]; else p[i]=g[l2++]; } for(int i=l;i<=r;i++) g[i]=p[i]; } main() { n=gi(),s=gi(); for(int i=1;i<=n;i++){ T[i]=gi()+T[i-1]; F[i]=gi()+F[i-1]; g[i].id=i,g[i].k=-F[i],g[i].x=T[i]; } for(int i=1;i<=n;i++) f[i]=s*F[n]+T[i]*F[i],g[i].b=f[i]-s*F[i]; sort(g+1,g+n+1,cmp);solve(1,n); printf("%lld",f[n]); }