【BZOJ 3591】 最长上升子序列

来源:互联网 发布:linux命令日志文件 编辑:程序博客网 时间:2024/06/16 00:55

哎我还是太菜了,这么短的代码 断断续续 调了好久。

看数据范围 大概就是状压DP

题目名字是【最长上升子序列】 可以猜测是拿跑LIS的单调栈搞搞。

那么可以设计一个状态f[A][B]表示已选集合A,其中B是当前单调栈里的元素,显然栈里的元素升序排列,不用额外加一维状态表示栈中元素的排列。显然这可以优化成3n的状态表示。

转移大概就是枚举A补集中的每个元素x加到单调栈中某个位置,两边的元素保留。由于题目已经给出一个必选的LIS(记为C),并且DP做的最后的时候所有元素都会被加进来,所以我们只要在DP过程中排除掉非法情况就可以了,这里会产生非法情况当且仅当x属于C且加入x的时候跳过了另一个属于C的元素,也就是在C中,x的前一个元素未被加入已选集合A。可以预处理出g[i][j]表示已选集合i加入元素j是否合法。

还有一个不知道有多大用 的常数优化。就是限制一下B的大小使其不超过C的大小。

#include <bits/stdc++.h>using namespace std;const int N=15;int n,m,S,T,pre[N],bit[1<<N],pow3[N],g[1<<N][N],d[1<<N][N],f[14348908],h[1<<N];int main(){    //freopen("aa.in","r",stdin);    int i,j,k,x,y;    scanf("%d%d",&n,&m);    for(y=-1,i=0;i<m;++i){        scanf("%d",&x);--x;        T|=(1<<x);pre[x]=y;y=x;    }    for(pow3[0]=i=1;i<n;++i)pow3[i]=pow3[i-1]*3;    S=(1<<n)-1;    for(i=0;i<=S;++i)bit[i]=bit[i>>1]+(i&1);    for(i=0;i<=S;++i)for(j=0;j<n;++j)if(1<<j&i)h[i]+=pow3[j];    for(i=0;i<=S;++i)for(j=0;j<n;++j)if(!(1<<j&i)){        if(!((1<<j)&T))g[i][j]=1;        else{           for(k=pre[j];~k;k=pre[k])if(!(1<<k&i))break;           if(k<0)g[i][j]=1;        }        if(bit[((1<<(j+1))-1)&i]<m){           for(k=1<<(j+1);k<=i&&!(k&i);k<<=1);           d[i][j]=1<<j|i;           if(k<=i)d[i][j]^=k;         }    }    f[0]=1;    for(i=0;i<=S;++i)for(k=0;k<n;++k)if(g[i][k]){        for(j=i;;j=(j-1)&i){            if(f[h[i]+h[j]]&&d[j][k])f[h[1<<k|i]+h[d[j][k]]]+=f[h[i]+h[j]];            if(!j)break;        }    }    int ans=0;    for(i=1;i<=S;++i)if(bit[i]<=m)ans+=f[h[S]+h[i]];    printf("%d",ans);    return 0;}
原创粉丝点击