题解 【P7919 [Kubic] ABC】

题意:对于一个长度为 \(n\) 的字符串 \(S \in \{\texttt{A,B,C}\}\),给定一个操作,对于三个字符 \(pA,pB,pC\in\{\texttt{A,B,C}\}\),使得 \(S\) 中属于区间 \([l,r]\) 的字符中所有 \(\texttt{A}\to pA\),所有 \(\texttt{B}\to pB\),所有 \(\texttt{C}\to pC\)。求使得 \(S\) 中任意两个相邻字符不同的最小操作次数及其中一个构造方案。

显然,要通过区间长度大于一的操作使得 \(S\) 中任意两个相邻字符不同,必然有 \(pA \not= pB \not= pC\),否则可能会导致该区间内的的相邻相等字符变多。

我们容易知道,对于这样的一次操作,区间内部除了两端的字符,其他位置的相邻字符是否相等的状况是不会发生改变的。所以我们得出结论:一次操作只能让一个或两个原本相邻相同的字符变得不再相同。

根据上面这个结论,若设字符串中满足 \(S_i=S_{i+1}\) 的位置个数为 \(t\),则最小操作次数为 \(\left \lceil \dfrac{t}{2} \right \rceil\) 次(因为如果剩下一个必须要多用一次操作)。

接下来考虑如何构造方案。根据上面的结论,设配对的满足 \(S_i=S_{i+1}\) 位置分别为 \(l\)\(r\),则为了将两端相同的相邻字符变得不同,我们需要对区间 \([l+1,r]\) 进行一次操作,将 \(S_{l+1}\)\(S_r\) 变得与 \(S_{l}\)\(S_{r+1}\) 不同。再接着讨论 \(pA,pB,pC\) 的取值,若 \(S_l \not= S_r\),我们只要将这两个字符互相变成对方即可(如 \(\texttt{A}\to\texttt{B},\texttt{B}\to\texttt{A}\),输出操作 BAC,理由显然);若 \(S_l = S_r\),则考虑轮换(即 \(\texttt{A}\to\texttt{B},\texttt{B}\to\texttt{C},\texttt{C}\to\texttt{A}\),输出操作 BCA),以避免 \(S_{l+2}\)\(S_{r-1}\) 可能对答案造成的影响。

对于当 \(t\) 为奇数时剩下的最后一组,考虑特殊处理。设最后一组为 \(S_k=S_{k+1}\),同理的,我们只需要改变 \(S_{k+1}\) 即可。通过讨论 \(S_{k+1}\)\(S_{k+2}\) 的关系及字符情况即可得到具体变换方式,只要最后变换后的 \(S_{k+1}\)\(S_{k+2}\) 不相等即可。

(当然,根据官方题解,每一次变换都采取轮换即能满足所有条件,而剩下的最后一组只要将区间延长到 \([k+1,n]\) 即可规避讨论。终归是想麻烦了(悲))

int n;
char s[N],op2[5]="BCA";
int p[N],cnt;

inline void Solve()
{
	scanf("%d%s",&n,s+1);
	if(n==1){printf("0\n");return;}
	for(int i=1;i<n;i++) if(s[i]==s[i+1]) p[++cnt]=i;
	printf("%d\n",(cnt+1)/2);
	int i;
	for(i=1;i<=cnt-1;i+=2)
	{
		char op[5]="ABC";
		int l=p[i],r=p[i+1];
		if(s[l+1]!=s[r])
		{
			swap(op[s[l+1]-'A'],op[s[r]-'A']);
			printf("%d %d %s\n",l+1,r,op);
		}
		else
		{
			printf("%d %d %s\n",l+1,r,op2);
		}
	}
	if(cnt&1)
	{
		int l=p[cnt];
		char op[5]="ABC";
		if(l==n-1 || s[l+1]==s[l+2])
		{
			printf("%d %d %s\n",l+1,l+1,op2);
		}
		else
		{
			if(s[l+1]=='A'&&s[l+2]=='B') strcpy(op,"CAB");
			if(s[l+1]=='B'&&s[l+2]=='A') strcpy(op,"ACB");
			if(s[l+1]=='A'&&s[l+2]=='C') strcpy(op,"BAC");
			if(s[l+1]=='C'&&s[l+2]=='A') strcpy(op,"ACB");
			if(s[l+1]=='B'&&s[l+2]=='C') strcpy(op,"BAC");
			if(s[l+1]=='C'&&s[l+2]=='B') strcpy(op,"BCA");
			printf("%d %d %s\n",l+1,l+1,op);
		}
	}
	return ;
}
/*
官方题解思路实现
inline void Solve()
{
	scanf("%d%s",&n,s+1);
	if(n==1){printf("0\n");return;}
	for(int i=1;i<n;i++) if(s[i]==s[i+1]) p[++cnt]=i;
	if(cnt&1) p[++cnt]=n;
	printf("%d\n",cnt/2);
	for(int i=1;i<=cnt;i+=2)
	{
		int l=p[i],r=p[i+1];
		printf("%d %d %s\n",l+1,r,op);
	}
	return ;
}
*/
posted @ 2021-11-02 09:13  Coinred  阅读(104)  评论(0编辑  收藏  举报