zoukankan      html  css  js  c++  java
  • [TJOI2018] 游园会

    一、题目

    点此看题

    二、解法

    考虑字符串计数 \(dp\) 的常见模型,设 \(dp(i,...,k)\) 表示已经填入了 \(i\) 个字符,现在串已经匹配到了 \(\tt NOI\) 长度为 \(k\) 的前缀,那么我们还需要把最长公共子序列记录到状态里面。

    考虑最长公共子序列的求法是普通 \(dp\),设 \(f(i,j)\) 表示考虑 \(A\) 串长度为 \(i\) 的前缀和 \(B\) 串长度为 \(j\) 的前缀,它们的最长公共子序列长度,这里我们让 \(A\) 串是我们正在处理的串:

    \[f(i,j)=\max\{f(i-1,j),f(i,j-1),f(i-1,j-1)+(A_i=B_j)\} \]

    考虑用一个自动机来表示这个 \(f\),自动机上的每一个节点都对应一个 \(f\),然后我们把原来的状态记成 \(dp(i,j,k)\) 表示匹配到了自动机上的第 \(j\) 个节点,这和 \(\tt AC\) 自动机、后缀自动机上 \(dp\) 类似,只不过自己设计自动机要更加灵活。

    可以考虑记 \(f\) 的差分数组,这样自动机上的节点数一共只有 \(2^{15}\) 种,外层 \(dp\) 转移枚举填入 NOI 的哪一种字符,内层 \(dp\) 转移直接由上一层推出下一层即可,时间复杂度 \(O(nk\cdot 2^k)\)

    这种用另一个 \(dp\) 检验合法,设计自动机记录状态的方法,我们称之为 \(dp\)\(dp\)

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 20;
    const int N = 32780;
    const int MOD = 1e9+7;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,a[M],b[M],ans[M],siz[N],dp[2][N][3];char s[M];
    int encode(int *a)
    {
    	int r=0;
    	for(int i=0;i<m;i++)
    		r|=(a[i+1]-a[i])<<i;
    	return r;
    }
    void decode(int *a,int r)
    {
    	for(int i=0;i<m;i++) a[i+1]=(r>>i)&1;
    	for(int i=1;i<=m;i++) a[i]+=a[i-1];
    }
    void trans(int w,int r,int p,char c,int v)
    {
    	decode(a,r);
    	for(int i=1;i<=m;i++)
    		b[i]=max(max(a[i],b[i-1]),a[i-1]+(c==s[i]));
    	int tr=encode(b);
    	dp[w][tr][p]=(dp[w][tr][p]+v)%MOD;
    }
    signed main()
    {
    	n=read();m=read();
    	scanf("%s",s+1);dp[0][0][0]=1;
    	for(int i=1;i<(1<<15);i++) siz[i]=siz[i>>1]+(i&1);
    	for(int i=0;i<n;i++)
    	{
    		int w=(i&1),tw=w^1;
    		for(int j=0;j<(1<<m);j++)
    			for(int p=0;p<3;p++)
    				dp[tw][j][p]=0;
    		for(int j=0;j<(1<<m);j++)
    		{
    			if(dp[w][j][0])
    			{
    				trans(tw,j,1,'N',dp[w][j][0]);
    				trans(tw,j,0,'O',dp[w][j][0]);
    				trans(tw,j,0,'I',dp[w][j][0]);
    			}
    			if(dp[w][j][1])
    			{
    				trans(tw,j,1,'N',dp[w][j][1]);
    				trans(tw,j,2,'O',dp[w][j][1]);
    				trans(tw,j,0,'I',dp[w][j][1]);
    			}
    			if(dp[w][j][2])
    			{
    				trans(tw,j,1,'N',dp[w][j][2]);
    				trans(tw,j,0,'O',dp[w][j][2]);
    			}
    		}
    	}
    	for(int i=0;i<(1<<m);i++)
    		for(int p=0;p<3;p++)
    			ans[siz[i]]=(ans[siz[i]]+dp[n&1][i][p])%MOD;
    	for(int i=0;i<=m;i++)
    		printf("%d\n",ans[i]);
    }
    
  • 相关阅读:
    树上莫队学习笔记
    点分治学习笔记
    7.11总结
    线段树合并学习笔记
    7.10总结
    bzoj1201: [HNOI2005]数三角形----递推+bitset
    bitset(01串)优化
    Tarjan系列1
    bsgs(Baby Steps Giant Steps)算法
    [SD2015]序列统计——solution
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15790173.html
Copyright © 2011-2022 走看看