题解:AT_agc055_d [AGC055D] ABC Ultimatum

0.前言

省流:275307894a 对 蒟蒻们 使用了 模拟赛
蒟蒻们 rp++

1.思路

双倍经验的部分分,我的题解
由于串固定,所以可以先枚举 ABC,BCA,CAB 的数量。
容易想到枚举每个 A,B,C 的位置
显然,第一个字符要作为一个字符串的第一个字符,最后一个字符要作为一个字符串的最后一个字符
故考虑贪心
ABC 的数量=\(A\)BCA 的数量=\(B\)CAB 的数量=\(C\),则方案如下

ABC BCA CAB
第一个字符所在位置 \((1,A)\) \((1,B)\) \((1,C)\)
第二个字符所在位置 \((B+1,B+A)\) \((C+1,C+B)\) \((A+1,A+C)\)
第三个字符所在位置 \((C+B+1,C+B+A)\) \((A+C+1,A+C+B)\) \((B+A+1,B+A+C)\)

这里的位置指的是相对位置。
形式化地,如设所有 A 中的第 \(i\)A\(a_i\),则 \((1,A)\)ABCA 应出现在 \((a_1,a_A)\) 范围内。
也即,当每个串最前面的字符出现在每种字符的最前面,最后的字符出现在每种字符的最后面时,若不存在方案,则说明 \(A,B,C\) 不是一个可行的方案。

省流:前面在前面,后面在后面,如果没方案,肯定没方案

具体证明:

点击查看

反证,假设存在一种方案在 \(A,B,C\) 的约束下成立,但在以上方案不成立,则可以通过数次交换 ABC,BCA,CAB 中的 A,B,C,使其最终化为此方案
我手搓的样例

ABCCAB

如选取 \(a_1,b_1,c_1\) 组成 ABC,\(c_2,a_2,b_2\) 组成 CAB
则显然 \(a_1,b_1,c_2\) 组成 ABC,\(c_1,a_2,b_2\) 组成 CAB 一定也是一个可行的方案。
故,在以上方案成立是当前方案成立的充分必要条件

省流:上一句话是对的

于是,你就得到了一种在 \(O(n^3)\) 时间内求出一个方案是否成立的方法
接下来,考虑优化枚举过程。
考虑给定\(A\), \(B\) 如何计数,则上面的最优分配方案可以转化为对于\(p_a,i\)\(p_b,B+i\) 等的顺序限制。
然后设 \(f_{a,b,c}\) 表示当前填了前 \(a\)A\(b\)B\(c\)C 的方案数。这个dp是 \(O(n^3)\) 的。
对于一般的情况,只需要枚举哪些子集对是合法的,容斥即可计算答案。
注意到如果两个子集 \(max\) 加起来大于 \(n\) 肯定是无解的,因此只有
\(O(2^nn)\) 个子集对需要计算,时间复杂度 \(O(2^nn^4)\),可以通过。

2.代码

是从双倍经验的代码改过来的,故有点丑。

#include <bits/stdc++.h>
using namespace std;
const int N=20,mod=998244353;
using ui=unsigned;
int n;
ui ans,dp[N][N][N][4];
char a[N*3],s1[N]="11111111111111111",s2[N]="11111111111111111";
int main() {
	scanf("%d%s",&n,a+1);
	ans=0;
	for(int x=0; x<=n; ++x) {
		for(int z=0; z<=n; ++z) {
			int Z=z;
			while(Z<=n&&s1[Z]=='0')Z++;
			int X=x;
			while(X<=n&&s2[X]=='0')X++;
			int y=n-X-Z;
			if(y<0)continue;
			dp[0][0][0][0]=1;
			for(int i=0,t; i<=n; ++i) {
				for(int j=0; j<=n && j<=i+x; ++j) {
					for(int k=max(i-z,0); k<=n && k<=j+y; ++k) {
						t=i+j+k;
						if(!t)continue;
						memset(dp[i][j][k],0,sizeof(dp[i][j][k]));
						if(i && j-i<x && (a[t]=='A' || a[t]=='?')) {
							dp[i][j][k][0]+=dp[i-1][j][k][0];
							dp[i][j][k][1]+=dp[i-1][j][k][1];
							dp[i][j][k][2]+=dp[i-1][j][k][2];
							dp[i][j][k][3]+=dp[i-1][j][k][3];
						}
						if(j && k-j<y && (a[t]=='B' || a[t]=='?')) {
							dp[i][j][k][0]+=dp[i][j-1][k][0];
							dp[i][j][k][1]+=dp[i][j-1][k][1];
							dp[i][j][k][2]+=dp[i][j-1][k][2];
							dp[i][j][k][3]+=dp[i][j-1][k][3];
						}
						if(k && i-k<z && (a[t]=='C' || a[t]=='?')) {
							dp[i][j][k][0]+=dp[i][j][k-1][0];
							dp[i][j][k][1]+=dp[i][j][k-1][1];
							dp[i][j][k][2]+=dp[i][j][k-1][2];
							dp[i][j][k][3]+=dp[i][j][k-1][3];
						}
						if(j-i==x) {
							dp[i][j][k][1]+=dp[i][j][k][0];
							dp[i][j][k][3]+=dp[i][j][k][2];
							dp[i][j][k][0]=dp[i][j][k][2]=0;
						}
						if(i-k==z) {
							dp[i][j][k][2]+=dp[i][j][k][0];
							dp[i][j][k][3]+=dp[i][j][k][1];
							dp[i][j][k][0]=dp[i][j][k][1]=0;
						}
						// cerr<<dp[i][j][k][0]<<' '<<dp[i][j][k][1]<<' '<<dp[i][j][k][2]<<' '<<dp[i][j][k][3]<<'\n';
					}
				}
			}
			ans+=dp[n][n][n][3];
			ans%=mod;
		}
	}
	printf("%u\n",ans);
	return 0;
}

3.后记

  1. 膜拜巨佬 275307894a
  2. 双倍经验,而且呈包含关系
  3. 给好不容易搞懂此题的本蒟蒻一个赞吧
posted @ 2025-07-11 19:15  yzc_is_SadBee  阅读(23)  评论(0)    收藏  举报