bzoj1597[Usaco2008 Mar]土地购买
题意:
n块土地,现在要求把土地分成几份,每份费用为该份中土地长最大值和宽最大值成绩,要求最小费用。n≤5000
题解:
当一块土地长宽都比另一块土地小时,这块土地可以当作另一块土地的附属品,对答案不影响。因此先按长第一关键字,宽第二关键字排序,然后用单调队列就可以把长宽都被覆盖的土地除去。之后剩在单调队列里的土地长是升序排列,宽是降序排列,故用斜率优化dp:f[i]=max(f[j]+长[i]*宽[j+1]),j比k好当且仅当(f[j]-f[k])/(宽[k+1]-宽[j+1])<长[i]。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 50100 6 #define ll long long 7 using namespace std; 8 9 struct str{ll x,y;}; str strs1[maxn],strs2[maxn]; 10 bool cmp(str a,str b){return a.x!=b.x?a.x<b.x:a.y<b.y;} 11 inline int read(){ 12 char ch=getchar(); int f=1,x=0; 13 while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} 14 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); 15 return f*x; 16 } 17 int n,l,r,m,q[maxn]; ll f[maxn]; 18 double calc(int j,int k){ 19 return (double)(f[j]-f[k])/(double)(strs2[k+1].y-strs2[j+1].y); 20 } 21 int main(){ 22 n=read(); inc(i,1,n)strs1[i].x=(ll)read(),strs1[i].y=(ll)read(); sort(strs1+1,strs1+n+1,cmp); m=0; 23 inc(i,1,n){while(m&&strs2[m].y<=strs1[i].y)m--; strs2[++m]=strs1[i];} l=1; r=1; q[l]=0; 24 inc(i,1,m){ 25 while(l<r&&calc(q[l],q[l+1])<strs2[i].x)l++; f[i]=f[q[l]]+strs2[i].x*strs2[q[l]+1].y; 26 while(l<r&&calc(q[r-1],q[r])>calc(q[r],i))r--; q[++r]=i; 27 } 28 printf("%lld",f[m]); return 0; 29 }
20160612