首页 > 其他分享 >[TJOI2018] 游园会 题解

[TJOI2018] 游园会 题解

时间:2024-10-15 22:00:10浏览次数:8  
标签:状态 LCS int 题解 数组 游园会 text TJOI2018

T7 [TJOI2018] 游园会

只能说是道有意思的好题。

一般来说遇到这种题我们想到的都会是设 \(f_{i,\dots}\) 表示长度为 \(i\),然后后面跟一堆状态的情况。此题需要我们满足两个条件:

  • LCS 的长度;
  • 不能出现 \(\texttt{NOI}\) 的子串。

第二个限制我们可以通过状态设计来解决,但第一个很棘手,因为我们没有办法通过枚举 LCS 的长度来转移。这里就引入了这道题的重头:DP of DP。我们可以通过状压把 LCS 数组的状态设到转移方程里。可以这么做的理由如下:

观察 LCS 的转移方法:

\[\text{LCS}_{i,j}=\max\begin{cases}\text{LCS}_{i-1,j-1}+1&t_i=s_j\\\max(\text{LCS}_{i,j-1},\text{LCS}_{i-1,j})&t_i\ne s_j\end{cases} \]

我们发现:

  1. 转移只涉及 \(i-1\) 行和 \(i\) 行,故可以滚动数组处理;
  2. \(\text{LCS}_{i,j}-\text{LCS}_{i,j-1}\le1\),说明原数组差分后是一个 \(\texttt{01}\) 串。所以可以状压。

所以我们定义状态:\(f_{i,s,l}\) 表示考虑到第 \(i\) 位、\(\text{LCS}\) 数组状态为 \(s\)、与 \(\texttt{NOI}\) 的匹配长度为 \(l\) 的方案数。之后转移容易。

代码中我们需要实现三个函数:

  • 编码函数(encode),把数组压成一个状态;
  • 解码函数(decode),把状态解码成数组;
  • 计算函数(trans),把状态解码、转移、再编码。

可以结合代码理解。

#include<bits/stdc++.h>
using namespace std;

constexpr int MOD=1e9+7;
int n,k;
string s;
void add(int&x,int y){
	x=x+y>=MOD?x+y-MOD:x+y;
}
int f[2][1<<15][3],g[2][16],ans[16],p[300],cnt[1<<15];
void decode(int s){
	for(int i=0;i<=k;++i)g[0][i]=0;
	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)s|=(g[1][i]-g[1][i-1])<<(i-1);
	return s;
}
void trans(int cur,int S,int l,int c,int x){
	if(!x||(l==2&&c==2))return;
	int nxt=l==0?1:0;
	if(!l&&!c)nxt=1;
	else if(l==1&&c==1)nxt=2;
	decode(S);
	for(int i=0;i<=k;++i)g[1][i]=0;
	for(int i=1;i<=k;++i){
		if(p[(int)s[i]]==l)g[1][i]=max(g[1][i],g[0][i-1]+1);
		g[1][i]=max({g[1][i],g[0][i],g[1][i-1]});
	}
	add(f[cur][encode()][nxt],x);
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>k>>s;
	p['N']=0,p['O']=1,p['I']=2;
	s=' '+s;
	f[0][0][0]=1;
	for(int i=1;i<=n;++i){
		for(int s=0;s<1<<k;++s)
			for(int l=0;l<3;++l)
				f[i&1][s][l]=0;
		for(int s=0;s<1<<k;++s)
			for(int l=0;l<3;++l){
				trans(i&1,s,l,0,f[(i&1)^1][s][0]);
				trans(i&1,s,l,1,f[(i&1)^1][s][1]);
				trans(i&1,s,l,2,f[(i&1)^1][s][2]);
			}
	}
	for(int s=0;s<1<<k;++s)cnt[s]=cnt[s>>1]+(s&1);
	for(int s=0;s<1<<k;++s)
		for(int l=0;l<3;++l)
			add(ans[cnt[s]],f[n&1][s][l]);
	for(int i=0;i<=k;++i)cout<<ans[i]<<'\n';
	return 0;
}

标签:状态,LCS,int,题解,数组,游园会,text,TJOI2018
From: https://www.cnblogs.com/laoshan-plus/p/18468608

相关文章

  • [JSOI2018] 潜入行动 题解
    T6[JSOI2018]潜入行动很套路、很裸的一道树形DP。看了状态就会推方程的那种。设\(f_{u,i,0/1,0/1}\)表示以\(u\)为根的子树中有\(i\)个监听器、\(u\)有没有监听器、\(u\)有没有被监听的方案数。显然要枚举子节点\(v\)、\(u\)的监听器数量\(i\)、\(v\)的监听器数......
  • [ABC213G] Connectivity 2 题解
    [ABC213G]Connectivity2题解套路的经典图上计数题。考虑枚举和\(1\)相连的子集\(S\)。答案显然由两部分构成,\(S\)集合和\(1\)相连的方案数\(f(S)\)和\(S\)对于\(G\)的补集所有的方案数\(g(S)\)。答案就是二者相乘。显然\(g\)更好处理。直接枚举集合的边即可......
  • P8386 [PA2021] Od deski do desk 题解
    P8386[PA2021]Oddeskidodesk题解考虑一个大的序列一定被分成几个区间来删除。朴素的dp定义是\(dp_{i,j}\)表示前\(i\)个数,最后一个数元素是\(j\)的方案数。然而这样不仅不好转移,而且设不下状态。不难发现所有值是等价的。考虑这样一个事情:若我们要分出一个新的区......
  • Project Euler 638 题解
    q-analog,老玩家集体起立!这也就是说:\[\binom{n+m}{n}_q=\sum_{\pi\inL_{n,m}}q^{area(\pi)}\]结束!#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongconstintmod=1e9+7,maxn=2e7+5;intqp(inta,intb,intp=mod){ intres=1; while(b){ i......
  • 2024某校新生校队选拔赛题解
    游记某校某院与某院联合培养机制给排了两场讲座,讲完吃饭,讲座时间\(8:00-12:00,\)拖堂\(10\)min,到食堂\(10\)min,吃饭\(30\)min,走回去\(10\)min,打开网址发现时间只够看榜的一看榜我草\(5\)小时连\(4\)个题都做不出来翻题面发现A,L纯签到,B半签到,遂确信成分题解题目链接A验证是......
  • Project Euler 457 题解
    初等数论小题目求\[n^2-3n-1\equiv0\pmod{p^2}\]配方,得到:\[(2n-3)^2\equiv13\pmod{p^2}\]根据亨泽尔引理,只需得到\((2n-3)^2\equiv13\pmod{p}\)的解即可提升到\(p^2\)。这是二次剩余,直接解。单次求解\(O(\logn)\),时间复杂度\(O(n)\)。#include<bits/stdc++.h......
  • TopCoder SRM616 ColorfulCoins 题解
    TopCoderSRM616ColorfulCoins题意给一套货币,保证任意两种货币存在倍数关系且颜色互不相同。已知货币的币值集合,每次可以询问一个金额,给出货币张数最小的表示方案。问最小的询问次数,使得通过已知信息可以对应货币面值和颜色。分析最大的面值问一个\(\inf\)即可。这时只需要......
  • [ABC062C]/[ARC074A] Chocolate Bar 题解
    神秘分讨题(?总共\(4\)中情况。第一种:三个竖的并列:ans=min(ans,(h%3>0)*w);。第二种:三个横的并列:ans=min(ans,(w%3>0)*h);。第三种:一个横的矩形,然后是两个竖着的。For(i,1,h){ intfst=i*w; intscd=(w/2)*(h-i); intthd=(w%2>0)*(h-i)+(w/2)*(h-i); ans=min(ans......
  • 堆排序题解
    给定一个整数序列,请按非递减序输出采用堆排序的各趟排序后的结果。输入格式:测试数据有多组,处理到文件尾。每组测试数据第一行输入一个整数n(1≤n≤100),第二行输入n个整数。输出格式:对于每组测试,输出若干行,每行是一趟排序后的结果,每行的每两个数据之间留一个空格。输入样例......
  • 「题解」Educational Codeforces Round 170 (Rated for Div. 2)
    before我不想写作业呜呜。A.TwoScreensProblemA.TwoScreensSol&Code理解题意后发现使用复制的方法完成最长公共前缀即可。#include<bits/stdc++.h>typedeflonglongll;typedefstd::pair<int,int>pii;intT;std::strings1,s2;intmain(){scanf("%d"......