BZOJ5336 TJOI2018 party 【状压DP】*
BZOJ5336 TJOI2018 party
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。
我们发先在对lsm进行计算的时候相邻两个DP值的差最多是一,所以我们借此进行状态构造。把一个dp值差分后的01串作为状态,然后对这个状态进行DP,因为我们需要考虑的特殊情况只有NOI,所以添加一维记一下状态就好了
#include<bits/stdc++.h>
using namespace std;
#define K 20
#define N 1010
#define S (1<<16)
#define Mod 1000000007
int n,k,bit[K],ans[K]={0};
int now[K]={0},res[K]={0},a[K];
int T[S][3]={0};
int dp[2][3][S]={0},ind=0,frm=1;
/*
dp数组中第二维
0->没有特殊限制
1->末尾是N
2->末尾是NO
*/
/*
N->0
O->1
I->2
*/
void add(int &x,int y){x=(x+y)%Mod;}
int countsiz(int s){int cnt=0;while(s)cnt++,s-=s&-s;return cnt;}
int main(){
scanf("%d%d",&n,&k);
getchar();
for(int i=1;i<=k;i++){
char c;scanf("%c",&c);
if(c=='N')a[i]=0;
if(c=='O')a[i]=1;
if(c=='I')a[i]=2;
}
bit[1]=1;for(int i=2;i<=k;i++)bit[i]=bit[i-1]<<1;
int up=(1<<k)-1;
for(int s=0;s<=up;s++){
for(int i=1;i<=k;i++)
now[i]=now[i-1]+(bool)(s&bit[i]);
for(int typ=0;typ<3;typ++){
for(int i=1;i<=k;i++)
if(a[i]==typ)res[i]=now[i-1]+1;
else res[i]=max(res[i-1],now[i]);
for(int i=1;i<=k;i++)T[s][typ]|=bit[i]*(res[i]-res[i-1]);
}
}
dp[ind][0][0]=1;
for(int i=1;i<=n;i++){
ind^=1;frm^=1;
memset(dp[ind],0,sizeof(dp[ind]));
for(int s=0;s<=up;s++){
//没有限制->没有限制(当前出现O或I)
add(dp[ind][0][T[s][1]],dp[frm][0][s]);
add(dp[ind][0][T[s][2]],dp[frm][0][s]);
//没有限制->限制1(当前出现N)
add(dp[ind][1][T[s][0]],dp[frm][0][s]);
//限制1->没有限制(当前出现I)
add(dp[ind][0][T[s][2]],dp[frm][1][s]);
//限制1->限制1(当前出现N)
add(dp[ind][1][T[s][0]],dp[frm][1][s]);
//限制1->限制2(当前出现O)
add(dp[ind][2][T[s][1]],dp[frm][1][s]);
//限制2->没有限制(当前出现O)
add(dp[ind][0][T[s][1]],dp[frm][2][s]);
//限制2->限制1(当前出现N)
add(dp[ind][1][T[s][0]],dp[frm][2][s]);
}
}
for(int s=0;s<=up;s++)
for(int i=0;i<3;i++)
add(ans[countsiz(s)],dp[ind][i][s]);
for(int i=0;i<=k;i++)printf("%d\n",ans[i]);
return 0;
}