仅记录我不太熟悉 及 有待加强的类型。
因为动态规划对于题目有较强依赖性,所以结合题目一起写了。
\(dp\) 套 \(dp\)
[TJOI2018]游园会
考虑设计一个自动机处理\(LCS\)的限制。
设\(f_{i,j,k}\)为前\(i\)个字符,转移到了自动机的\(j\)节点,匹配到了\(NOI\)的第\(k\)位,即可以转移了。
那么考虑如何构建这个自动机。
\(g_{i,j}\)为第一个串匹配到\(i\),第二个串匹配到\(j\),由于第一个串是我们转移的串,转移只需知道\(g_{i - 1,x}\)和新加入的字符即可,考虑记录\(g_{i,x}\)为状态,状压其差分数组即可。
考虑每一次都是从上一层转移到下一层,我们无需直接构建该自动机,在\(dp\)时处理其即可。
[TJOI2018]游园会
#include<bits/stdc++.h>
#define ll long long
#define N 1010
#define mod 1000000007
#define popcount(S) __builtin_popcount(S)
int n,k,tot,ans[N];
char s[N];
int f[2][1 << 15][3],g[2][N];
void decode(int S) {
for (int i=1;i<=k;++i) g[0][i]=(S>>(i-1))&1;
for (int i=1;i<=k;++i) g[0][i]+=g[0][i-1];
}
int encode() {
int S=0;
for (int i=1;i<=k;++i)
if (g[1][i]-g[1][i-1]) S|=1<<(i-1);
return S;
}
void trans(int nxt,int S,int l,char c,int w) {
decode(S);
for (int i=1;i<=k;++i) {
g[1][i]=std::max(g[1][i-1],g[0][i]);
if (c==s[i]) g[1][i]=std::max(g[1][i],g[0][i-1]+1);
}
int j=encode();
f[nxt][j][l]=(f[nxt][j][l]+w)%mod;
}
int main(){
scanf("%d%d",&n,&k);
scanf("%s",s + 1);
tot = (1ll << k);
f[0][0][0] = 1;
for(int i = 0;i < n;++i){
int now = i & 1;
int nxt = 1^now;
memset(f[nxt],0,sizeof(f[nxt]));
for(int j = 0;j < tot;++j){
if(f[now][j][0]){
trans(nxt,j,1,'N',f[now][j][0]);
trans(nxt,j,0,'O',f[now][j][0]);
trans(nxt,j,0,'I',f[now][j][0]);
}
if(f[now][j][1]){
trans(nxt,j,1,'N',f[now][j][1]);
trans(nxt,j,2,'O',f[now][j][1]);
trans(nxt,j,0,'I',f[now][j][1]);
}
if(f[now][j][2]){
trans(nxt,j,1,'N',f[now][j][2]);
trans(nxt,j,0,'O',f[now][j][2]);
}
}
}
for (int i=0;i<tot;++i)
for (int j=0;j<3;++j)
ans[popcount(i)]=(ans[popcount(i)]+f[n&1][i][j])%mod;
for (int i=0;i<=k;++i) printf("%d\n",ans[i]);
}