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;
}

  

posted @ 2015-07-17 13:40  Claris  阅读(1198)  评论(0编辑  收藏  举报