AtCoder AGC046D Secret Passage (结论、DP)

题目链接

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]) 累加进答案的充要条件是存在一个 (kle ple 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
",i,j,k,f[i][j][k]);
				updsum(ans,f[i][j][k]);
			}
		}
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/suncongbo/p/14674621.html