首页 > 其他分享 >【洛谷】P4590 [TJOI2018]游园会(dp套dp)

【洛谷】P4590 [TJOI2018]游园会(dp套dp)

时间:2023-03-20 18:56:12浏览次数:58  
标签:include LCS int 游园会 字符串 TJOI2018 dp mod

原题链接

题意

对于一个长度为 \(n\) 的仅由 \(N,O,I\) 组成且不包含字串 \(NOI\) 的字符串 \(S\),其与一个给定的长度为 \(K\) 的字符串的最长公共子序列为 \(LCS\)。

求出对于 \(LCS=0 \sim K\),一共有多少种合法的字符串 \(S\)。

\(n \leq 1000,K \leq 15\)。

思路

对于朴素的 \(\mathrm{LCS}\),有一种很经典的动态规划做法,设 \(g_{i,j}\) 为第一个字符串中长度为 \(i\) 的前缀与第二个字符串中长度为 \(j\) 的前缀的 \(\mathrm{LCS}\),得到转移方程:

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

对于本题来说,还要求构造的字符串中不含有字串 \(NOI\),那么一种很直观的想法就是设 \(f_{i,g_{i,k},len}\) 前 \(i\) 个字符与给定字符串的最长公共子序列为 \(g_{i,k}\),且当前字符串的后缀匹配到字符串 \(NOI\) 的第 \(len\) 位的方案数。

对于这种外层 dp 的状态为内层 dp 的结果的转移方式,就被称为 dp 套 dp。

在转移的时候,根据 \(g_{i,j}\) 的转移方程,需要用到 \(j=0 \sim {k-1}\) 的所有 \(g_{i,j}\)。那么肯定不可能直接将数组放在转移状态中,注意到 \(K\) 的取值实际上也很小,可以用一个二进制数来代表 \(g_{i,j}\) 的状态,只需要令这个二进制数的第 \(i\) 位表示当前位置是否成功匹配即可(也可以看出是 \(g_{i,j}\) 的差分数组),在逆向生成 \(g\) 数组的时候只需要做一遍前缀和即可。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1010,M=17,mod=1e9+7;
int f[2][1<<M][3],g[2][N],n,m,ans[N];char s[N];
void Add(int &a,int b){a+=b;((a>=mod)&&(a-=mod));}
void decode(int S)
{
	for(int i=1;i<=m;i++) g[0][i]=(S>>i-1)&1;
	for(int i=1;i<=m;i++) g[0][i]+=g[0][i-1];
}
int encode()
{
	int S=0;for(int i=1;i<=m;i++) if(g[1][i]>g[1][i-1]) S|=1<<i-1;return S;
}
int count(int S){int res=0;while(S) res++,S-=S&-S;return res;}
void update(int nex,int S,int l,char c,int w)
{
	decode(S);
	for(int i=1;i<=m;i++)
	{
		g[1][i]=max(g[1][i-1],g[0][i]);
		if(c==s[i]) g[1][i]=max(g[1][i],g[0][i-1]+1);
	}
	S=encode();Add(f[nex][S][l],w);
}

int main()
{
	scanf("%d%d",&n,&m);scanf("%s",s+1);f[0][0][0]=1;
	for(int i=0;i<n;i++)
	{
		int now=i&1,nex=i+1&1;memset(f[nex],0,sizeof(f[nex]));
		for(int j=0;j<(1<<m);j++)
		{
			if(f[now][j][0])
			{
				update(nex,j,1,'N',f[now][j][0]);
				update(nex,j,0,'O',f[now][j][0]);
				update(nex,j,0,'I',f[now][j][0]);
			}
			if(f[now][j][1])
			{
				update(nex,j,2,'O',f[now][j][1]);
				update(nex,j,1,'N',f[now][j][1]);
				update(nex,j,0,'I',f[now][j][1]);
			}
			if(f[now][j][2])
			{
				update(nex,j,1,'N',f[now][j][2]);
				update(nex,j,0,'O',f[now][j][2]);
			}
		}
	}
	for(int i=0;i<(1<<m);i++) for(int j=0;j<3;j++) Add(ans[count(i)],f[n&1][i][j]);
	for(int i=0;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}
/*
3 2
NO
*/

标签:include,LCS,int,游园会,字符串,TJOI2018,dp,mod
From: https://www.cnblogs.com/ListenSnow/p/17237325.html

相关文章

  • wordpress外贸站,采用astra ,woocommerce,weglot等
              翻译搜索复制......
  • #创作者激励#[触觉智能RK3568]修改屏幕 DPI(像素密度)
    【本文正在参加2023年第一期优质创作者激励计划】(目录)触觉智能RK3568购买链接如下:https://item.taobao.com/item.htm?spm=4645b.1.14.1.5c4a4a7dv1soeZ&id=6587890390......
  • go 实现udp通信
    go实现udp通信 udp:不需要建立连接就能直接进行数据发送和接收,属于不可靠的、没有时序的通信,但是UDP协议的实时性比较好,通常用于视频直播相关领域。服务端实现代码:......
  • 【洛谷】P2150 [NOI2015] 寿司晚宴(状压dp+根号分治)
    原题链接题意有序列\(2,3,4\cdotsn\),对于序列中的每一个数,它可以被放入两个集合中的任意一个,或者不选。最后需要满足两个集合间的数两两互质(集合内部的数不需要满足互......
  • 树形dp注意事项
    1.树形dp的for循坏能优化就优化,比如取j=min(size[x],m),k<=min(size[x],m)之类的,否则很容易TLE2.要考虑清楚不合法状态是否会对答案产生影响,如果有就要memset(dp,-1,s......
  • 基础dp
    珂爱的dp们区间dp在\(dp\)的状态设计中,设计以区间为状态的\(dp\)或以区间为阶段进行的\(dp\)即为区间\(dp\),一般有最值问题和计数问题,一般方程为\[f[l][r][.........
  • 能不能说一说 TCP 和 UDP 的区别?
    TCP是一个面向连接的、可靠的、基于字节流的传输层协议。而UDP是一个面向无连接的传输层协议。和 UDP 相比,TCP有三大核心特性:面向连接。所谓的连接,指的是客户端和服......
  • Leetcode 5.最长回文子串(区间dp)
    题目链接在这里:5.最长回文子串-力扣(LeetCode)首先肯定是个n^2的算法,枚举起点也是必要的,但是枚举终点很显然不行,但是考虑到回文串会向下兼容,因此我们可以枚举长度,这就是......
  • 用户数据报协议 UDP
    用户数据报协议UDPUDP概述用户数据报协议UDP只在IP的数据报服务之上增加了很少一点的功能,这就是复用和分用的功能以及查错检测的功能UDP的主要特点UDP是无连......
  • Arm64v8 cpu + Centos7 aarch64中安装 Ambari 2.7.3 和 HDP 3.1.0
    #下载不存在的资源的方法使用迅雷云盘,添加下载任务到云盘,有一定的概率下载到已经被删除的资源。比如下载HDP相关的资源:<http://mirrors.huaweicloud.com/kunpeng/yum......