dp套dp学习笔记(P4590 [TJOI2018]游园会题解)

dp套dp学习笔记(P4590 [TJOI2018]游园会题解)

分析

题目的意思是对于每一个 \(i\) 求满足如下条件的字符串的数目:

\(1\).长度为 \(n\) 且只出现过'N','O','I'三种字符。

\(2\).和一个长度为 \(k\) 的模式串的最长公共子序列长度恰好为 \(i\)

\(3\).不含"NOI"这个子串。

发现第一个限制和第三个限制比较好满足,直接 \(dp\) 的时候记一下当前匹配到了 \(NOI\) 这个字符串的第几个字符即可。

对于第二个限制,我们似乎需要知道当前的字符串每一位填了什么数才可以求出它和模式串的最长公共子序列。

但是仔细想一下又会发现不同的公共子序列只有 \(2^k\) 种,所以我们只需要状压当前的串已经匹配好的位置的状态。

转移的时候我们需要对于每一种匹配的状态分别求出它加上三种不同的字符之后达到的新的匹配的状态。

这个东西可以提前用一次 \(dp\) 预处理出来。

转移的时候用我们预处理出来的状态直接转移即可。

大概可以理解为将内层 \(dp\) 的结果作为外层 \(dp\) 的状态进行 \(dp\)

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int mod=1e9+7,maxn=4e4+5;
int nxt[maxn][3],f[2][maxn][3],dp[maxn][2],n,m,a[maxn],mmax,siz[maxn],ans[maxn];
char s[maxn];
inline int addmod(rg int now1,rg int now2){
	return now1+=now2,now1>=mod?now1-mod:now1;
}
void pre(){
	for(rg int i=0;i<=mmax;i++){
		for(rg int k=1;k<=m;k++) dp[k][0]=dp[k-1][0]+((i>>(k-1))&1);
		for(rg int j=0;j<=2;j++){
			for(rg int k=1;k<=m;k++){
				dp[k][1]=std::max(dp[k-1][1],dp[k][0]);
				if(a[k]==j) dp[k][1]=std::max(dp[k][1],dp[k-1][0]+1);
			}
			rg int zt=0;
			for(rg int k=1;k<=m;k++) if(dp[k][1]>dp[k-1][1]) zt|=(1<<(k-1));
			nxt[i][j]=zt;
		}
	}
}
int main(){
	n=read(),m=read();
	scanf("%s",s+1);
	for(rg int i=1;i<=m;i++){
		if(s[i]=='O') a[i]=1;
		else if(s[i]=='I') a[i]=2;
	}
	mmax=(1<<m)-1;
	for(rg int i=1;i<=mmax;i++) siz[i]=siz[i>>1]+(i&1);
	pre();
	rg int now=1;
	f[0][0][0]=1;
	for(rg int i=1;i<=n;i++){
		now^=1;
		for(rg int j=0;j<=mmax;j++){
			for(rg int k=0;k<=2;k++){
				f[now^1][j][k]=0;
			}
		}
		for(rg int j=0;j<=mmax;j++){
			for(rg int k=0;k<=2;k++){
				if(f[now][j][k]){
					if(k==0){
						f[now^1][nxt[j][0]][1]=addmod(f[now^1][nxt[j][0]][1],f[now][j][k]);
						f[now^1][nxt[j][1]][0]=addmod(f[now^1][nxt[j][1]][0],f[now][j][k]);
						f[now^1][nxt[j][2]][0]=addmod(f[now^1][nxt[j][2]][0],f[now][j][k]);
					} else if(k==1){
						f[now^1][nxt[j][1]][2]=addmod(f[now^1][nxt[j][1]][2],f[now][j][k]);
						f[now^1][nxt[j][0]][1]=addmod(f[now^1][nxt[j][0]][1],f[now][j][k]);
						f[now^1][nxt[j][2]][0]=addmod(f[now^1][nxt[j][2]][0],f[now][j][k]);
					} else {
						f[now^1][nxt[j][0]][1]=addmod(f[now^1][nxt[j][0]][1],f[now][j][k]);
						f[now^1][nxt[j][1]][0]=addmod(f[now^1][nxt[j][1]][0],f[now][j][k]);
					}
				}
			}
		}
	}
	now^=1;
	for(rg int i=0;i<=mmax;i++){
		ans[siz[i]]=addmod(addmod(ans[siz[i]],f[now][i][0]),addmod(f[now][i][1],f[now][i][2]));
	}
	for(rg int i=0;i<=m;i++){
		printf("%d\n",ans[i]);
	}
	return 0;
}
posted @ 2021-03-25 20:41  liuchanglc  阅读(69)  评论(0编辑  收藏  举报