BZOJ1166 : [Baltic2008]Magical Stones

考虑二分答案,转化为求有多少$\leq lim$的数满足条件。

从两侧往中间进行数位DP,设$f[l][r][j][x][y][z][pre][suf]$表示当前准备填的两个位置是$l$和$r$,已经有$j$对相邻的数不同,$l-1$填的是$x$,$r+1$填的是$y$,正序串和逆序串的大小关系为$z$,正序串和$lim$的大小关系为$pre$,逆序串和$lim$的大小关系为$suf$的方案数。

注意到$r=n-l+1$,因此可以省去$r$这一维,然后枚举接下来填什么数进行转移。

时间复杂度$O(2^6n^2k)$。

 

#include<cstdio>
typedef long long ll;
const ll inf=1LL<<61;
const int N=65;
int n,m,i,j,S,l,r,x,y,z,p,s,nj,nz,np,ns,A,B,a[N];ll L,R,mid,K,ans=-1,f[N][N][1<<5];
inline void up(ll&a,ll b){a+=b;if(a>inf)a=inf;}
inline ll check(ll mid){
  for(i=n;i;i--)a[i]=mid&1,mid>>=1;
  for(i=1;i<=n;i++)for(j=0;j<=m;j++)for(S=0;S<32;S++)f[i][j][S]=0;
  ll ret=0;
  for(f[0][0][0]=l=1,r=n;l<=r;l++,r--)for(j=0;j<=m;j++)for(S=0;S<32;S++)if(f[l-1][j][S]){
    x=S&1,y=S>>1&1,z=S>>2&1,p=S>>3&1,s=S>>4&1;
    if(l<r)for(A=0;A<2;A++)for(B=0;B<2;B++){
      nj=j;
      if(l>1)nj+=(A^x)+(B^y);
      if(nj>m)continue;
      if(z)nz=1;
      else{
        if(A>B)continue;
        nz=A<B;
      }
      if(p)np=1;
      else{
        if(A>a[l])continue;
        np=A<a[l];
      }
      if(B==a[r])ns=s;else ns=B>a[r];
      up(f[l][nj][A|(B<<1)|(nz<<2)|(np<<3)|(ns<<4)],f[l-1][j][S]);
    }
    if(l==r)for(A=0;A<2;A++){
      if(l>1&&j+(A^x)+(A^y)>m)continue;
      if(!p&&(A>a[l]||A==a[l]&&s))continue;
      up(ret,f[l-1][j][S]);
    }
  }
  if(n%2==0)for(j=0;j<=m;j++)for(S=0;S<32;S++)if(f[l-1][j][S]){
    x=S&1,y=S>>1&1;
    if(j+(x^y)>m)continue;
    p=S>>3&1,s=S>>4&1;
    if(!p&&s)continue;
    up(ret,f[l-1][j][S]);
  }
  return ret;
}
int main(){
  scanf("%d%d%lld",&n,&m,&K);
  L=0;R=(1LL<<n)-1;
  while(L<=R)if(check(mid=(L+R)>>1)>=K)R=(ans=mid)-1;else L=mid+1;
  if(ans<0)puts("NO SUCH STONE");else for(i=n-1;~i;i--)putchar(ans>>i&1?'X':'I');
  return 0;
}

  

posted @ 2017-02-02 21:21  Claris  阅读(195)  评论(0编辑  收藏  举报