BZOJ3711 : [PA2014]Druzyny
设f[i]为[1,i]分组的最优解,则
f[i]=max(f[j]+1),max(c[j+1],c[j+2],...,c[i-1],c[i])<=i-j<=min(d[j+1],d[j+2],...,d[i-1],d[i])
设g[i]=min(j),i-j<=min(d[j+1],d[j+2],...,d[i-1],d[i])
容易发现g[i]单调不下降,可以通过线段树$O(n\log n)$预处理
考虑通过分治优化DP
在solve(l,r)时,求出[l+1,r]中c[]最大的位置,设为k
以k为分界线可以递归solve(l,k-1),solve(k,r)
然后只需用[l,k-1]的决策更新[k,r]即可
由于c[k]最大,所以c[k]<=i-j
i从max(c[k]+l,k)开始,决策j一开始的取值范围为[max(l,g[i]),i-c[k]]
每当i往右移一位时,j的上限也往右移一位,可以做到$O(1)$更新
j的下限可能也会右移到g[i],此时有l<=g[i]<=k-1,由于所有更新i的区间[l,k-1]均不相交
所以只存在一个区间[l,k-1]满足l<=g[i]<=k-1,即每个i最多只会发生一次下限右移
对于每次右移用线段树查询新区间内的最优解即可
当i循环到k+c[k]时,[k+c[k],r]内所有i的可行决策j的上限都为k-1,所以按g[]值将
[k+c[k],r]分割,对于每一段用线段树区间更新即可
总体复杂度为$O(n\log n)$。
#include<cstdio> const int N=1000010,M=2097153,P=1000000007,inf=-1000000; int n,i,j,c[N],d[N],g[N]; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} inline int min(int a,int b){return a<b?a:b;} inline int max(int a,int b){return a>b?a:b;} inline int merge(int a,int b){return c[a]>c[b]?a:b;} struct Num{ int x,y; Num(){x=y=0;} Num(int _x,int _y){x=_x,y=_y;} inline Num operator+(Num b){ if(x<b.x)return b; if(x>b.x)return Num(x,y); return Num(x,(y+b.y)%P); } inline Num operator+(int _x){return Num(x+_x,y);} inline void operator+=(Num b){*this=*this+b;} }f[N]; struct Node{int c,d;Num f,tag;}T[M]; void build(int x,int a,int b){ if(a==b){ T[x].c=a; T[x].d=d[a]; return; } int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid+1,b); T[x].c=merge(T[x<<1].c,T[x<<1|1].c); T[x].d=min(T[x<<1].d,T[x<<1|1].d); } void build2(int x,int a,int b){ T[x].tag=Num(inf,0); if(a==b){ T[x].d=d[a]; T[x].f=f[a]; return; } int mid=(a+b)>>1; build2(x<<1,a,mid),build2(x<<1|1,mid+1,b); T[x].d=min(T[x<<1].d,T[x<<1|1].d); T[x].f=T[x<<1].f+T[x<<1|1].f; } int askc(int x,int a,int b,int c,int d){ if(c<=a&&b<=d)return T[x].c; int mid=(a+b)>>1; if(d<=mid)return askc(x<<1,a,mid,c,d); if(c>mid)return askc(x<<1|1,mid+1,b,c,d); return merge(askc(x<<1,a,mid,c,d),askc(x<<1|1,mid+1,b,c,d)); } int askd(int x,int a,int b,int c,int d){ if(c>d)return n+1; if(c<=a&&b<=d)return T[x].d; int mid=(a+b)>>1; if(d<=mid)return askd(x<<1,a,mid,c,d); if(c>mid)return askd(x<<1|1,mid+1,b,c,d); return min(askd(x<<1,a,mid,c,d),askd(x<<1|1,mid+1,b,c,d)); } void add(int x,int a,int b,int c,int d,Num p){ if(c<=a&&b<=d){T[x].tag=T[x].tag+p;return;} int mid=(a+b)>>1; if(c<=mid)add(x<<1,a,mid,c,d,p); if(d>mid)add(x<<1|1,mid+1,b,c,d,p); } Num askf(int x,int a,int b,int c,int d){ if(c>d)return Num(inf,0); if(c<=a&&b<=d)return T[x].f; int mid=(a+b)>>1; if(d<=mid)return askf(x<<1,a,mid,c,d); if(c>mid)return askf(x<<1|1,mid+1,b,c,d); return askf(x<<1,a,mid,c,d)+askf(x<<1|1,mid+1,b,c,d); } inline Num askf1(int c){ int x=1,a=0,b=n,mid;Num t=Num(inf,0); while(a!=b){ t+=T[x].tag; mid=(a+b)>>1,x<<=1; if(c<=mid)b=mid;else a=mid+1,x|=1; } return t+T[x].tag; } void change(int x,int a,int b,int c,Num p){ if(a==b){T[x].f=p;return;} int mid=(a+b)>>1; if(c<=mid)change(x<<1,a,mid,c,p);else change(x<<1|1,mid+1,b,c,p); T[x].f=T[x<<1].f+T[x<<1|1].f; } inline void update(int l,int k,int r){ int i=max(c[k]+l,k); if(g[i]>=k||i>r)return; int jl=max(l,g[i]),jr=i-c[k]; Num tmp=askf(1,0,n,jl,jr)+1; for(;i<=k-1+c[k]&&i<=r;i++){ if(g[i]>jl){ if(g[i]>=k)return; jl=g[i]; tmp=askf(1,0,n,jl,jr)+1; } f[i]+=tmp; jr++; if(jr>=jl)tmp+=f[jr]+1; } while(i<=r){ if(g[i]>jl){ if(g[i]>=k)return; jl=g[i]; } tmp=askf(1,0,n,jl,k-1)+1; int t=askd(1,0,n,jl+1,n); if(t>r){ add(1,0,n,i,r,tmp); return; } add(1,0,n,i,t-1,tmp); i=t; } } void solve(int l,int r){ if(l==r){ if(l)change(1,0,n,l,f[l]=f[l]+askf1(l)); return; } int k=askc(1,0,n,l+1,r); solve(l,k-1); update(l,k,r); solve(k,r); } int main(){ read(n); for(i=1;i<=n;i++)read(c[i]),read(d[i]); build(1,0,n); for(i=0;i<=n;i++)d[i]=n+1,f[i]=Num(inf,0); f[0]=Num(0,1); for(i=0;i<=n;i++){ while(j<i&&i-j>askd(1,0,n,j+1,i))j++; g[i]=j; if(d[g[i]]>n)d[g[i]]=i; } build2(1,0,n); solve(0,n); if(f[n].x>0)printf("%d %d",f[n].x,f[n].y);else puts("NIE"); return 0; }