BZOJ2587 : [Ceoi2011]Team

将球员按限制从大到小排序,那么最优解中每支球队都是一个连续的区间。

设$f[i]$表示前$i$大的球员成功组队时,最多能组的队伍数,$g[i]$表示此时最大人数的最小值。

那么$f[i]=\max(f[j]+1),a[j]\geq i-j$,即$a[j]+j\geq i$。

注意到$a[j]+j>j$恒成立,所以可以使用链表来维护,当计算$f[a[j]+j]$时,才将$j$加入决策范围中。

此时需要维护决策中$f$的最大值,对于多个最大值,则要让$\max(g[j],i-j)$最小。

$1.$若$g[j]\leq i-j$,即$g[j]+j\leq i$,那么同样可以使用链表来维护,$j$最大的是最优决策。

$2.$若$g[j]>i-j$,那么这些$j$还在后面的链表里,使用堆来维护$g[j]$最小的$j$即可。

时间复杂度$O(n\log n)$。

 

#include<cstdio>
const int N=1000010,BUF=10000000,OUT=10000000;
char Buf[BUF],*buf=Buf,Out[OUT],*ou=Out;int Outn[30],Outcnt;
int n,i,j,x=-1,a[N],b[N],c[N],f[N],g[N],w[N],g0[N],nxt0[N],g1[N],nxt1[N],h[N],l;
inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
inline void write(int x){
  for(Outcnt=0;x;x/=10)Outn[++Outcnt]=x%10+48;
  while(Outcnt)*ou++=Outn[Outcnt--];
}
inline void swap(int&a,int&b){int c=a;a=b;b=c;}
inline bool cmp(int x,int y){
  if(f[x]!=f[y])return f[x]>f[y];
  return g[x]<g[y];
}
inline void pop(){
  h[1]=h[l--];
  for(int i=1;;){
    int t=h[i],j=0;
    if((i<<1)<=l&&cmp(h[i<<1],t))t=h[j=i<<1];
    if((i<<1|1)<=l&&cmp(h[i<<1|1],t))j=i<<1|1;
    if(j)swap(h[i],h[j]),i=j;else return;
  }
}
inline void add0(int x){
  int y=x+b[x+1];
  if(y>n)return;
  nxt0[x]=g0[y];g0[y]=x;
}
inline void add1(int x){
  h[++l]=x;
  for(int i=l;i>1&&cmp(h[i],h[i>>1]);i>>=1)swap(h[i],h[i>>1]);
  int y=x+g[x];
  if(y>n)return;
  nxt1[x]=g1[y];g1[y]=x;
}
inline void up(int y){
  if(x<0){x=y;return;}
  if(f[y]>f[x]||f[y]==f[x]&&y>x)x=y;
}
inline void use(int y){
  int z=g[y]>i-y?g[y]:i-y;
  if(f[y]+1>f[i])f[i]=f[y]+1,g[i]=z,w[i]=y;
  else if(f[y]+1==f[i]&&z<g[i])g[i]=z,w[i]=y;
}
int main(){
  fread(Buf,1,BUF,stdin);read(n);
  for(i=1;i<=n;i++)read(a[i]),b[a[i]]++;
  for(i=n;i;i--)b[i]+=b[i+1];
  for(i=1;i<=n;i++)c[b[a[i]]--]=i;
  for(i=1;i<=n;i++)b[i]=a[c[i]],f[i]=g0[i]=g1[i]=-1;
  for(add0(f[0]=0),i=1;i<=n;i++){
    for(j=g0[i];~j;j=nxt0[j])if(g[j]+j<=i)up(j);else add1(j);
    for(j=g1[i];~j;j=nxt1[j])up(j);
    if(~x)use(x);
    while(l){
      j=h[1];
      if(g[j]+j>i){use(j);break;}
      pop();
    }
    if(f[i]>0)add0(i);
  }
  write(f[n]);*ou++='\n';
  while(n){
    j=w[n];
    write(n-j);
    for(i=j+1;i<=n;i++)*ou++=' ',write(c[i]);
    *ou++='\n';
    n=j;
  }
  fwrite(Out,1,ou-Out,stdout);
  return 0;
}
posted @ 2017-03-23 02:47  Claris  阅读(252)  评论(0编辑  收藏  举报