Luogu P4590 [TJOI2018]游园会

Link
求LCS有一个显然的(O(n^2))的dp做法,即设(f_{i,j})表示(s_i,t_j)的lcs长度,那么(f_{i,j}=max(f_{i-1,j},f_{i,j-1},f_{i-1,j-1}+[s_i=t_j]))
(t)是题目给定的,因此在已知(f_{i-1},s_i)的情况下我们是可以求出(f_i)的。
因此我们考虑将(f_i)当做是一个状态并把它放在自动机上,那么根据转移方程我们就可以的得到自动机上的转移进而求出答案。
同时我们知道(f_i)是单调不降的,且相邻两项的差要么为(0)要么为(1),因此我们可以用(Delta f_i)来表示(f_i),这样自动机上的点数就只有(2^m)了。
而且LCS的长度就是(Delta f_i)(1)的个数,这样我们就可以方便地统计了。
然后设(f_{i,j,k})为长度为(i)的字符串,在自动机上(j)号点,匹配( ext{NOI})长度为(k)时的方案数,预处理出自动机上的转移,然后滚掉(i)这一维即可。

#include<cstdio>
#include<cstring>
const int P=1000000007;const char ccf[]="NOI";
int f[2][1<<15|1][3],trans[1<<15|1][3],ans[16];char str[16];
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
int main()
{
    int n,m,now=0;
    scanf("%d%d%s",&n,&m,str),f[0][0][0]=1;
    for(int i=0;i<1<<m;++i)
	for(int j=0;j<3;++j)
	{
	    int las=-1,to=i;
	    for(int k=m-1;~k;--k) if(i>>k&1) las=k; else if(str[k]==ccf[j]) (~las? to^=1<<las:0),las=k,to^=1<<k;
	    trans[i][j]=to;
	}
    for(int i=1;i<=n;++i,now^=1)
    {
	memset(f[now^1],0,sizeof f[now^1]);
	for(int j=0;j<1<<m;++j) for(int k=0;k<3;++k) if(f[now][j][k]) for(int l=0;l<3;++l) if(k^2||l^2) inc(f[now^1][trans[j][l]][k==l? k+1:!l],f[now][j][k]);
    }
    for(int i=0;i<1<<m;++i) for(int j=0;j<3;++j) inc(ans[__builtin_popcount(i)],f[now][i][j]);
    for(int i=0;i<=m;++i) printf("%d
",ans[i]);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/13023587.html