bzoj5336: [TJOI2018]party【状压dp】

Description

小豆参加了NOI的游园会,会场上每完成一个项目就会获得一个奖章,奖章 只会是N, O, I的字样。在会场上他收集到了K个奖章组成的串。
兑奖规则是奖章串和兑奖串的最长公共子序列长度为小豆最后奖励的等级。
现在已知兑奖串长度为N,并且在兑奖串上不会出现连续三个奖章为NOI,即奖章中不会出现子串NOI。
现在小豆想知道各个奖励等级会对应多少个不同的合法兑奖串。

Input

第一行两个数,N,K分别代表兑奖串的长度,小豆收集的奖章串的长度。
第二行一共K个字符,表示小豆得到奖章串。
N<=1000 & K<=15

Output

一共K+1行,第i行表示小豆最后奖励等级为i-1的不同的合法兑奖串的个数,可能这个数会很大,结果对10^9 + 7取模。

Sample Input

3 2

NO

Sample Output

1

19

6

提示

最长公共子序列长度0的串有:III;

最长公共子序列长度2的串有:NON, NNO, NOO, ONO,

INO, NIO;

除去NOI,余下的19(26-6-1)种为最长公共子序列长度为1。

解题思路:

该题是状态压缩dp,也有称为基于dp的dp。
首先不考虑条件不出现NOI,我们发现的是我们需要统计的LCS值,可以通
过dp处理出来的。那么考虑,如果考虑到兑奖串第i位的时候,如果知道之
前lcs这个dp的状态,就可以求出这位取不同值的时候lcsdp的状态。考虑在
做lcs的时候,可以知道的是相邻的两个dp值的差不超过1,我们就吧这层
中lcsdp的状态用他们的差来记录,这样就使得状态有2^k 次种,k为奖章串的
长度,然后考虑这2^k 状态的转移,每种有四种情况。
最后复杂度为O(2^k × N)
现在在考虑NOI不能出现,只需要在原来的基础上加上一维即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1005,mod=1e9+7;
inline void add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
int n,m,S,tr[1<<15][3],f[2][1<<15][3],bin[1<<15],trl[3][3],g[20],h[20],ans[20];
char ch[20],c[3];
void decode(int s)
{
    g[0]=0;
    for(int i=1;i<=m;i++)g[i]=g[i-1]+(s>>i-1&1);
}
int incode()
{
    int res=0;
    for(int i=1;i<=m;i++)res|=(h[i]-h[i-1])<<i-1;
    return res;
}
void pre()
{
    S=1<<m;c[0]='N',c[1]='O',c[2]='I';
    trl[0][0]=1,trl[0][1]=0,trl[0][2]=0;
    trl[1][0]=1,trl[1][1]=2,trl[1][2]=0;
    trl[2][0]=1,trl[2][1]=0,trl[2][2]=3;
    for(int s=1;s<S;s++)bin[s]=bin[s>>1]+(s&1);
    for(int j=0;j<3;j++)
        for(int s=0;s<S;s++)
        {
            decode(s);memset(h,0,sizeof(h));
            for(int i=1;i<=m;i++)
                h[i]=c[j]==ch[i]?g[i-1]+1:max(h[i-1],g[i]);
            tr[s][j]=incode();
        }
}
int main()
{
    //freopen("party.in","r",stdin);
    //freopen("party.out","w",stdout);
    scanf("%d%d%s",&n,&m,ch+1);
    pre();f[0][0][0]=1;
    for(int i=0,now=0;i<n;i++,now^=1)
        for(int s=0;s<S;s++)
            for(int k=0;k<3;k++)if(f[now][s][k])
            {
                for(int j=0;j<3;j++)if(trl[k][j]<3)
                    add(f[now^1][tr[s][j]][trl[k][j]],f[now][s][k]);
                f[now][s][k]=0;
            }
    for(int s=0;s<S;s++)
        for(int j=0;j<3;j++)add(ans[bin[s]],f[n&1][s][j]);
    for(int i=0;i<=m;i++)cout<<ans[i]<<'\n';
    return 0;
}


猜你喜欢

转载自blog.csdn.net/cdsszjj/article/details/80392345