AGC036E ABC String

AGC036E

有个由\(A,B,C\)组成的字符串,要找到其中最长的一个子序列,满足:

\(A,B,C\)出现次数相等。

子序列中相连的字母不同。

\(|S|\le 10^6\)


似乎杂题的时候遇见过呢。。。

这题是个乱搞好题,反正看网上若干篇博客都感觉不一样。

这里说说我的乱搞做法:

显然有这样一条性质:对于一个字符串来说,如果有个子序列满足相连的字母不同,那么它一定可以通过如此操作:每次删去形如\(BAC\)中的\(A\),或者\(BAB\)中的\(AB\)(或\(BA\))得到,并且满足删的过程中一直满足相连的字母不同。

接下来假设\(cnt_A\ge cnt_B \ge cnt_C\)

假如只删\(AB\)\(AC\),设\(z\)为每一个字母剩下的出现次数,那么有\(cnt_A-z=cnt_B-z+cnt_C-z\),即\(z=cnt_B+cnt_C-cnt_A\)

我们先只做删\(A\)的操作,再只做\(AB\)\(AC\)的操作。那么我们就要最小化\(cnt_A\)(当然最小也要大于等于\(cnt_B\)),所以在\(cnt_A>cnt_B\)时尽量找可以删的\(A\),然后删之。

假如删去的所有的可以删的\(A\),那么不存在形如\(BAC\)的结构,只会存在\(BABAB\dots AB\)这样的。可以发现,无论后面怎么操作,都不会出现必须做删\(A\)操作才能够到达的状态(也就是说即使有也可以通过删\(AB\)之类的替换),所以只操作删\(AB\)操作在当前状况下是最优的。这时候\(z\)可以直接取到\(cnt_B+cnt_C-cnt_A\)。证明:设\(L_B,L_C\)分别为可以删\(AB\)\(AC\)最多次数。如果\(z\)符合条件,必有\(B-z\le L_B\),也就是\(A-L_B\le C\),此时存在的\(A\)要么夹在\(B\)之间要么夹在\(C\)之间,\(A-L_B\)意味着夹在\(C\)之间的\(A\)的个数,所以显然不会超过\(C\)\(L_C\)的证明同理。设\(S_A\)表示删\(A\)操作的最多次数,那么\(cnt_B+cnt_C-cnt_A+S_A\)是此时的答案。这样在当前状况下得到的最优的答案。

这同时也是全局状况下最优解的答案,证明(这里的证明比较感性,不够严谨,期待更好的证明):假如我们通过这个方法确定了一个方案,不难发现改变操作顺序没有任何影响,所以这是只有删\(A\)\(AB\)\(AC\)操作下的最优解。如果要更优,那么就可能要引入删\(B\)\(C\)甚至\(BC\)的操作。不考虑删\(C\)\(BC\)操作(这种操作一眼就感觉不存在。。。),设删\(B\)操作要做\(x\)次,删\(A\)操作要做\(y\)次。那么有\(z=cnt_C+cnt_B-cnt_A+y-x\),如果它更优,就有\(y-x>S_A\),所以\(y>x+S_A\),之前定义了\(S_A\)是删\(A\)操作的最多次数,要是满足这个条件,这个最多次数一定在操作过程中发生了增加,增加无非就是\(BABC\to BAC\)这种形式,又因为\(BAC\)中的\(A\)是消耗品,所以最终变成\(BC\)。这和直接删\(AB\)是等价的。于是引入\(B\)操作就是相当于\(AB\)拆成删\(B\)和删\(A\),没有本质突破。所以最优解由删\(A\)\(AB\)\(AC\)的得到,即上面所述答案。

假如没有删去所有可以删的\(A\),那么这时候一定有\(cnt_A=cnt_B\ge cnt_C\)\(z\)可以取到\(cnt_B+cnt_C-cnt_A=cnt_C\),以下证明\(cnt_A-cnt_C\le L_B\):变一下就是\(cnt_A-L_B-L_C\le cnt_C-L_C\),这样可以等价成一个只有所有\(A\)都在形如\(BAC\)形式的字符串,且不存在\(ABA\)\(ACA\)。假如不成立,由于一个\(BAC\)带着一个\(A\)\(C\),我们希望\(cnt_A'>cnt_C'\),自然是要让\(C\)被出现在两组\(BAC\)之中,就会出现形如\(BACAB\)的结构,然而这个时候是存在了一个\(ACA\)的,于是就矛盾了。证完之后,我们得到:如果只删\(AB\),那么能够达到答案是\(cnt_C\)。显然\(cnt_C\)同时也是答案的上限,所以这样在当前状况下也能得到最优答案,同时也是全局状况下的最优答案。

综上,我们得到了一个策略:先做删\(A\)的操作,删到不能删或者\(cnt_A=cnt_B\)为止。然后取\(z=cnt_B+cnt_C-cnt_A\),适当得删些\(AB\)\(AC\)。这样就得到了一个正确的做法。

话说有些证明都是我写题解的时候脑补出来的呢,当时切题的时候拍了好久没有问题就直接干了。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 1000010
int n;
char str[N];
int cnt[3],p[3],re[3];
void adjust(int &A,int &B,int &C){
	memset(cnt,0,sizeof cnt);
	for (int i=1;i<=n;++i)
		cnt[str[i]-'A']++;
	for (int i=0;i<3;++i)
		p[i]=i;
	for (int i=0;i<3;++i)
		for (int j=1;j<3;++j)
			if (cnt[p[j-1]]<cnt[p[j]])
				swap(p[j-1],p[j]);
	for (int i=0;i<3;++i)
		re[p[i]]=i;
	for (int i=1;i<=n;++i)
		str[i]=re[str[i]-'A']+'A';
	A=cnt[p[0]],B=cnt[p[1]],C=cnt[p[2]];
}
struct LIST{
	LIST *pre,*suc;
	char c;
} s[N];
void erase(LIST *p){
	p->pre->suc=p->suc;
	p->suc->pre=p->pre;
}
bool BAC(LIST *p){return p->c=='A' && p->pre->c!=p->suc->c;}
bool BAB(LIST *p){return !p->pre->c || !p->suc->c || p->pre->c==p->suc->c;}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%s",str+1);
	n=strlen(str+1);
	int tmp=0;
	for (int i=1;i<=n;++i)
		if (str[i]!=str[i-1])
			str[++tmp]=str[i];
	n=tmp;
	str[n+1]=0;
	int A,B,C;
	adjust(A,B,C);
	if (C==0)
		return 0;
//	printf("%s\n",str+1);
	s[0].suc=&s[1];
	s[n+1].pre=&s[n];
	for (int i=1;i<=n;++i)
		s[i]={&s[i-1],&s[i+1],str[i]};
	for (LIST *p=s[0].suc;p!=&s[n+1] && A>B;p=p->suc)
		if (BAC(p)){
			A--;
			erase(p);			
		}	
//	for (LIST *p=s[0].suc;p!=&s[n+1];p=p->suc)
//		putchar(p->c);
//	printf("\n");
	int z=B+C-A;
	int neB=B-z,neC=C-z;
	for (LIST *p=s[0].suc;p!=&s[n+1];p=p->suc){
		if (BAB(p)){
//			for (LIST *p=s[0].suc;p!=&s[n+1];p=p->suc)
//				putchar(p->c);
//			printf("\n");
			if (p->c=='A'){
				if ((p->pre->c|p->suc->c)=='B')
					{if (neB) erase(p->pre->c?p->pre:p->suc),erase(p),neB--;}
				else
					{if (neC) erase(p->pre->c?p->pre:p->suc),erase(p),neC--;}
			}
			else if ((p->pre->c|p->suc->c)=='A'){
				if (p->c=='B')
					{if (neB) erase(p->pre->c?p->pre:p->suc),erase(p),neB--;}
				else
					{if (neC) erase(p->pre->c?p->pre:p->suc),erase(p),neC--;}
			}
		}
	}
	for (LIST *q=s[0].suc;q!=&s[n+1];q=q->suc)
		putchar(p[q->c-'A']+'A');
	return 0;
}
	
posted @ 2020-09-17 22:04  jz_597  阅读(179)  评论(0编辑  收藏  举报