把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ3864】Hero meet devil(DP套DP)

点此看题面

大致题意: 给定一个字符串,对于每一个\(i\),问有多少由"A","G","T","C"组成的长度为\(m\)的字符串和给定串的最长公共子序列长度为\(i\)

双倍经验

可以去看看【洛谷4590】[TJOI2018] 游园会,就会发现与那题相比此题甚至还要少一个限制。

这里直接给出代码了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define M 15
#define X 1000000007
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,l,s[M+5];char st[M+5];
class LCS_Automation
{
	private:
		int lim,a[M+5],f[M+5],S[1<<M][4],cnt[1<<M],dp[2][1<<M];
		I void Extend(CI x)//求出x的后继状态
		{
			RI i;for(i=0;i^l;++i) a[i+1]=a[i]+((x>>i)&1);//解码
			RI j,t;for(i=0;i^4;++i)//枚举加入字符
			{
				for(j=1;j<=l;++j) f[j]=max(max(a[j],f[j-1]),a[j-1]+(s[j]==i));//DP
				for(t=j=0;j^l;++j) t|=(f[j+1]-f[j])<<j;S[x][i]=t;//状压差值
			}
		}
	public:
		I void Build() {lim=1<<l;for(RI i=0;i^lim;++i) Extend(i);}//建立LCS自动机
		int ans[M+5];I void DP()//LCS自动机上DP
		{
			#define Cls(t) memset(dp[t],0,sizeof(dp[t]))
			RI i,j,k,t;for(Cls(0),dp[0][0]=1,t=i=0;i^n;++i,t^=1)//注意滚存
				for(Cls(t^1),j=0;j^lim;++j) for(k=0;k^4;++k) Inc(dp[t^1][S[j][k]],dp[t][j]);//枚举加入字符转移
			for(memset(ans,0,sizeof(ans)),i=0;i^lim;++i)//显然cnt[i]就是状态i的LCS
				cnt[i]=cnt[i>>1]+(i&1),Inc(ans[cnt[i]],dp[t][i]);//统计答案
		}
}A;
int main()
{
	RI Tt,i;scanf("%d",&Tt);W(Tt--)
	{
		scanf("%s%d",st+1,&n),l=strlen(st+1);
		for(i=1;i<=l;++i) s[i]=st[i]^'A'?(st[i]^'G'?(st[i]^'T'?3:2):1):0;//把字符改为数码
		for(A.Build(),A.DP(),i=0;i<=l;++i) printf("%d\n",A.ans[i]);//建自动机,然后自动机上DP
	}return 0;
}
posted @ 2020-06-15 12:38  TheLostWeak  阅读(119)  评论(0编辑  收藏  举报