题目链接

https://atcoder.jp/contests/agc046/tasks/agc046_d

题解

细节调了一万年系列。

假设操作后长度为 \(l\),进行了 \(n-l\) 次操作。那么显然原串的最后 \((2l-n)\) 个字符是最终的串的子序列,因为这些数根本没有动过。
那么最终串剩下的数全都由前面删除的数插入而来,所以还有一个条件是存在一种将前面的数经过 \((n-l)\) 次操作之后删除的 \(0\)\(1\) 的个数恰好与最终串相对应的方案。
容易证明这两个条件就足够了。第一个条件显然可以设 \(f[i][j][k]\) 表示长度为 \(i\) 的串,一共 \(j\)0,从后往前匹配到原串第 \(k\) 位的方案数。
然而重点是需要仔细考虑一下第二个条件的细节。
首先我们需要一个 DP 来求这个东西。设 \(dp[i][j][k]\) 表示当前操作了原串前 \(i\) 位,一共删除了 \(j\)0\(k\)1 的方案数。转移详见代码。
然后考虑什么样的 \(f\) 可以加进答案。我们的要求是进行 \((n-l)\) 次操作,最后 \((2l-n)\) 位不能动,对于一个 \(f[i][j][k]\) 来讲,原串中可以保留的位置是 \([k,n]\), 或者 \([k+1,n]\),..., 或者 \([lim,n]\),其中 \(lim=\min(n+1,n-(2l-n)+1)\). 若保留位置是 \([p,n]\),那就意味着操作进行到 \(p-1\) 这个点。所以 \(f[i][j][k]\) 累加进答案的充要条件是存在一个 \(k\le p\le lim\) 使得 \(dp[p-1][tot_0-j][tot_1-(i-j)]=1\),即“操作到前 \((k-1)\) 位,删掉 \(0\)\(1\) 的个数恰好满足 \(i,j\) 的要求”是可行的。

时间复杂度 \(O(n^3)\).

试错了无数次,还看了题解,血的教训/ll

代码

#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define y0 asdfiaewoet
#define y1 dawurpoewiu
#define tm paisjropsdj
#define x first
#define y second
#define iter iterator
#define riter reverse_iterator
#define pll pair<llong,llong>
using namespace std;

inline int read()
{
	int x = 0,f = 1; char ch = getchar();
	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') {f = -1;}}
	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
	return x*f;
}

const int mxN = 300;
const int P = 998244353;
int n; char a[mxN+3];
llong f[mxN+3][mxN+3][mxN+3];
int cnt[mxN+3][2]; bool dp[mxN+3][mxN+3][mxN+3],can[mxN+3][mxN+3];

void updsum(llong &x,llong y) {x += (x+y>=P?y-P:y);}

int main()
{
	scanf("%s",a+1); n = strlen(a+1); for(int i=1; i<=n; i++) a[i] -= 48;
	for(int i=1; i<=n; i++) {cnt[i][0] = cnt[i-1][0],cnt[i][1] = cnt[i-1][1]; cnt[i][a[i]]++;}
	dp[0][0][0] = true;
	for(int i=0; i<=n; i++)
	{
		for(int j=0; j<=i; j++) for(int k=0; k+j<=i; k++) if(dp[i][j][k])
		{
			//if "dp[i+1][j][k] = true;" then you are wrong
			if(cnt[i][0]-j>=2) {dp[i][j+1][k] = true;}
			if(cnt[i][1]-k>=2) {dp[i][j][k+1] = true;}
			if(cnt[i][0]-j>=1&&cnt[i][1]-k>=1) {dp[i][j+1][k] = true,dp[i][j][k+1] = true;}
			if(i+1<=n&&cnt[i][0]-j>=1) {dp[i+1][j+(a[i+1]==0)][k+(a[i+1]==1)] = true,dp[i+1][j+1][k] = true;}
			if(i+1<=n&&cnt[i][1]-k>=1) {dp[i+1][j+(a[i+1]==0)][k+(a[i+1]==1)] = true,dp[i+1][j][k+1] = true;}
			if(i+2<=n) {dp[i+2][j+(a[i+2]==0)][k+(a[i+2]==1)] = true,dp[i+2][j+(a[i+1]==0)][k+(a[i+1]==1)] = true;}
		}
	}
	f[0][0][n+1] = 1ll;
	for(int i=0; i<=n; i++)
	{
		for(int j=0; j<=i; j++)
		{
			for(int k=n+1; k>=n-i+1; k--)
			{
				updsum(f[i+1][j+1][k-(a[k-1]==0)],f[i][j][k]);
				updsum(f[i+1][j][k-(a[k-1]==1)],f[i][j][k]);
			}
		}
	}
	llong ans = 0ll;
	for(int i=1; i<=n; i++)
	{
		for(int j=0; j<=i; j++) if(j<=cnt[n][0]&&i-j<=cnt[n][1])
		{
			int lim = min(n+1,2*n-2*i+1); bool ok = false;
			for(int k=lim; k>=n-i+1; k--)
			{
				ok |= dp[k-1][cnt[n][0]-j][cnt[n][1]-(i-j)]; if(!ok) continue;
//				printf("f[%d][%d][%d]=%lld\n",i,j,k,f[i][j][k]);
				updsum(ans,f[i][j][k]);
			}
		}
	}
	printf("%lld\n",ans);
	return 0;
}