题解 石头剪刀布

plaese kill me. && don't forget me.


题目描述

给定 \(n\) 个字符串 \(s_i\) 只包含 0,1,2,现在要捏一个序列 \(A\)\(s_i\) 表示 \(a_i\) 可以捏成什么。1,2,3 形成环形吊打关系,\(\omega(X)\) 表示序列 \(X\) 最长的吊打子序列,吊打序列指的是对于 \(a_{p_1},\dots,a_{p_k}\) ,除了首位每一位都被前一个吊打。求对于 \(t=1,\dots,n\) 满足 \(\omega(A)=t\)\(X\) 有多少个。

\(n\leq 2000\)


题解

先考虑给定一个序列 \(\omega(A)\) 怎么求,简单 dp,设 \(f[i][0/1/2]\) 表示前 \(i\) 位以 \(0/1/2\) 结尾的最长长度,假设 \(x\) 吊打 \(y\)\(f[i][y]=\max\{f[j][x]+1\}\)

发现关心 \(f[i][0/1/2]\),那么弄更好做的状态 \(g[i][x][y][z]\) 表示前 \(i\) 位以 \(0/1/2\) 结尾的吊打子序列最长为 \(x/y/z\) 的方案数,转移就跟自己和吊打 add1 取 max 就可以啦 qwq

然后到了喜闻乐见的优化环节,题目特性对于 \(x,y,z\) 有类似 \(|x-y|\leq 2\) 的关系,于是乎有用的状态就很少啦,把后两维换成 \(-2\to2\),第一位加个滚动就可以愉快的 AC 了捏。

#include<bits/stdc++.h>
#define int long long

using namespace std;

const int N=2023, P=998244353;
int n, lsy[N][4], ans[N];
int f[2][N][5][5];

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	for(int i=1; i<=n; ++i) {
		string s; cin >> s;
		for(int j=0; j<s.size(); ++j)
			lsy[i][s[j]-'0']=1;
	}
	f[0][0][2][2]=1;
	for(int i=1; i<=n; ++i) {
		int u=i%2, v=1-u;
		memset(f[u],0,sizeof(f[u]));
		for(int j=0; j<i; ++j)
			for(int a=0; a<5; ++a)
				for(int b=0; b<5; ++b) {
					if(!f[v][j][a][b]) continue;
					int x=j, y=j+a-2, z=j+b-2, qwq;
					if(lsy[i][0]) {
						qwq=max(x,z+1);
						f[u][qwq][y-qwq+2][z-qwq+2]+=f[v][j][a][b];
						f[u][qwq][y-qwq+2][z-qwq+2]%=P;
					}
					if(lsy[i][1]) {
						qwq=max(x+1,y);
						f[u][x][qwq-x+2][z-x+2]+=f[v][j][a][b];
						f[u][x][qwq-x+2][z-x+2]%=P;
					}
					if(lsy[i][2]) {
						qwq=max(y+1,z);
						f[u][x][y-x+2][qwq-x+2]+=f[v][j][a][b];
						f[u][x][y-x+2][qwq-x+2]%=P;
					}
				} 
	}
	for(int i=0; i<=n; ++i)
		for(int a=0; a<5; ++a)
			for(int b=0; b<5; ++b) {
				ans[max(max(i,i+a-2),i+b-2)]+=f[n%2][i][a][b];
				ans[max(max(i,i+a-2),i+b-2)]%=P;
			}
	for(int i=1; i<=n; ++i)
		cout << ans[i] << ' ';
	return 0;
}
posted @ 2023-08-17 21:03  Hypoxia571  阅读(13)  评论(0编辑  收藏  举报