ARC166A的题解

发布时间 2023-10-19 10:57:52作者: osfly

略带一点思维吧。

个人认为比 B 难想。

先来考虑弱化版的题面:


Case 1

如果 \(X\) 串和 \(Y\) 串都没有字母 C,如何判断是否有解?

观察操作,我们能发现这个操作的本质实际上是让一个位于前面的字母 A 挪到其后面的任意的位置,并且前后两个 A 的相对位置不会发生改变。

所以,如果:

  1. \(X\) 串与 \(Y\) 串长度相等。

  2. \(X\)A 的数量与 \(Y\) 串中 A 的数量相等。

  3. \(X\) 串和 \(Y\) 串中的 A 从前往后标号,每一组 \(X\) 串中的 A 的位置比其对应标号的 \(Y\)A 的位置靠前。

以上三种情况均满足时有解。

解释一下第三种情况。

假设:

\[\begin{aligned} X:ABAABB\\ Y:ABBABA\\ \end{aligned} \]

标号后为:

\[\begin{aligned} 1\ 2\ 3\ 4\ 5\ 6\ \\ X:ABAABB\\ 1\ \ \ \ 2\ 3\ \ \ \ \ \ \ \\ Y:ABBABA\\ 1\ \ \ \ \ \ \ \ 2\ \ \ \ 3\\ \end{aligned} \]

将对应标号的 A 的位置写出来为:

\[\begin{aligned} X:1\ 3\ 4\\ Y:1\ 4\ 6\\ \end{aligned} \]

我们发现,相同的标号,\(X\) 串的位置都比 \(Y\) 串的位置前。

所以这种情况下就是有解的。

再比如:

\[\begin{aligned} X:ABABAB\\ Y:ABAABB\\ \end{aligned} \]

标号后为:

\[\begin{aligned} 1\ 2\ 3\ 4\ 5\ 6\ \\ X:ABABAB\\ 1\ \ \ \ 2\ \ \ \ \ 3\ \ \ \\ Y:ABAABB\\ 1\ \ \ \ \ 2\ 3\ \ \ \ \ \ \ \\ \end{aligned} \]

将对应标号的 A 的位置写出来为:

\[\begin{aligned} X:1\ 3\ 5\\ Y:1\ 3\ 4\\ \end{aligned} \]

我们发现,相同的标号,第三组 \(X\) 串的位置比 \(Y\) 串的位置后。

所以这种情况下就是无解的。


Case 2

升级一下:

如果 \(X\) 串有 C\(Y\) 串没有 C,如何判断是否有解?

根据 Case 1,我们知道首先 \(X\) 串和 \(Y\)A 的数量要相等。

贪心地考虑,如果我们要使得 \(X\) 串能变成 \(Y\) 串,我们 A 越前越好。剩下的 C 就全部变成 B 就行了。

所以我们就能得出这一部分的有解的情况:

  1. \(X\) 串与 \(Y\) 串长度相等。

  2. \(X\)A 的数量加上 C 的数量要超过 \(Y\) 串的 A 的数量。

  3. \(X\)A 的数量不能超过 \(Y\) 串的 A 的数量。

  4. 若我们贪心地把 \(X\) 串用 C 来凑 A 使得 \(X\) 串和 \(Y\) 串的 A 的数量相等后,需要满足 Case 1 的条件 \(3\)


Case 3

就是原题了。

我们发现,如果存在一个 \(i\) 使得 \(X_i\) 不为 C\(Y_i\)C 时一定无解。

去除掉这种无解的情况后,\(Y\) 串剩余的 C 一定都能在 \(X\) 串对应的位置找到 C

考虑到操作不能使得 C 的位置交换,所以这些 C 我们不能把它们变成 A 或者 B

此时,整个 \(X\) 串和 \(Y\) 串就被我们切割成若干段。

我们考虑每一段,此时问题就退化成 Case 2 的版本了。

单次询问时间复杂度 \(O(n)\)

#include<cstdio>
const int N=2e5+10;
int T;
int n;
char s[N],t[N];
int ss[N],tt[N];
bool check()
{
	for(int i=1;i<=n;i++)
		if(s[i]!='C'&&t[i]=='C') return 0;
	int x[N],tot=0;
	x[++tot]=1;
	for(int i=1;i<=n;i++)//切割
		if(t[i]=='C'&&i!=1&&i!=n) x[++tot]=i;
	x[++tot]=n;
	for(int i=1;i<tot;i++)
	{
		int l=x[i],r=x[i+1];
		if(t[l]=='C') l++;
		if(t[r]=='C') r--;
		int sa=0,sc=0,ta=0;
		for(int j=l;j<=r;j++)
		{
			if(s[j]=='A') sa++;
			if(s[j]=='C') sc++;
			if(t[j]=='A') tt[++ta]=j;
		}
		if(sa+sc<ta) return 0;
		if(sa>ta) return 0;
		sc=ta-sa,sa=0;//计算出需要用多少个 C 去变成 A
		for(int j=l;j<=r;j++)
		{
			if(s[j]=='A') ss[++sa]=j;
			if(s[j]=='C'&&sc) ss[++sa]=j,sc--;//贪心地填补 A
		}
		for(int j=1;j<=sa;j++)
			if(ss[j]>tt[j]) return 0;
	}
	return 1;
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%s%s",&n,s+1,t+1);
		if(check()) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}