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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章