BZOJ2862 : 分糖果
二分答案$x$表示最大的一段的和。
设$f[i]$表示前$i$个最多分几段,满足最大的一段不超过$x$,若$f[n]\geq k$,则可行,
则$f[i]=\max(f[j])+1,sum[i]-sum[j]\leq x$。
用Treap优化DP,$O(n\log^2n)$。
同理再次二分得到最小的$x$,使得该限制下最少的段数$\leq k$。
则$ans=\max(x_0,x_1)$。
#include<cstdio> #include<cstdlib> const int N=20010,inf=1000000000; int n,k,i,l,r,mid,ans0,ans1,f,sum[N]; inline int max(int a,int b){return a>b?a:b;} inline int min(int a,int b){return a<b?a:b;} struct node{ int p,val,vmx,vmi,mx,mi;node*l,*r; node(){val=p=0,vmx=mx=-inf,vmi=mi=inf;l=r=NULL;} inline void up(){ mx=max(vmx,max(l->mx,r->mx)); mi=min(vmi,min(l->mi,r->mi)); } }*blank=new(node),pool[N],*cur=pool,*T; inline void Rotatel(node*&x){node*y=x->r;x->r=y->l;x->up();y->l=x;y->up();x=y;} inline void Rotater(node*&x){node*y=x->l;x->l=y->r;x->up();y->r=x;y->up();x=y;} void Ins(node*&x,int p,int v){ if(x==blank){ x=cur++;x->val=p;x->l=x->r=blank;x->vmx=x->mx=x->vmi=x->mi=v;x->p=std::rand(); return; } x->mx=max(x->mx,v); x->mi=min(x->mi,v); if(p==x->val){ x->vmx=max(x->vmx,v); x->vmi=min(x->vmi,v); return; } if(p<x->val){ Ins(x->l,p,v); if(x->l->p>x->p)Rotater(x); }else{ Ins(x->r,p,v); if(x->r->p>x->p)Rotatel(x); } } int Askmx(node*&x,int p){ if(x==blank)return -inf; if(p==x->val)return max(x->vmx,x->r->mx); if(p<x->val)return max(x->vmx,max(x->r->mx,Askmx(x->l,p))); return Askmx(x->r,p); } int Askmi(node*&x,int p){ if(x==blank)return inf; if(p==x->val)return min(x->vmi,x->r->mi); if(p<x->val)return min(x->vmi,min(x->r->mi,Askmi(x->l,p))); return Askmi(x->r,p); } int main(){ blank->l=blank->r=blank; scanf("%d%d",&n,&k); for(i=1;i<=n;i++)scanf("%d",&sum[i]),sum[i]+=sum[i-1]; l=-inf,r=ans0=ans1=inf; while(l<=r){ mid=(l+r)>>1; cur=pool,Ins(T=blank,0,0); for(i=1;i<=n;i++)Ins(T,sum[i],f=Askmx(T,sum[i]-mid)+1); if(f>=k)r=(ans0=mid)-1;else l=mid+1; } l=-inf,r=inf; while(l<=r){ mid=(l+r)>>1; cur=pool,Ins(T=blank,0,0); for(i=1;i<=n;i++)Ins(T,sum[i],f=Askmi(T,sum[i]-mid)+1); if(f<=k)r=(ans1=mid)-1;else l=mid+1; } return printf("%d",max(ans0,ans1)),0; }