AGC036E ABC String

AGC036E

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

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

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

(|S|le 10^6)


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

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

这里说说我的乱搞做法:

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

接下来假设(cnt_Age 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)的结构,只会存在(BABABdots AB)这样的。可以发现,无论后面怎么操作,都不会出现必须做删(A)操作才能够到达的状态(也就是说即使有也可以通过删(AB)之类的替换),所以只操作删(AB)操作在当前状况下是最优的。这时候(z)可以直接取到(cnt_B+cnt_C-cnt_A)。证明:设(L_B,L_C)分别为可以删(AB)(AC)最多次数。如果(z)符合条件,必有(B-zle L_B),也就是(A-L_Ble 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 o BAC)这种形式,又因为(BAC)中的(A)是消耗品,所以最终变成(BC)。这和直接删(AB)是等价的。于是引入(B)操作就是相当于(AB)拆成删(B)和删(A),没有本质突破。所以最优解由删(A)(AB)(AC)的得到,即上面所述答案。

假如没有删去所有可以删的(A),那么这时候一定有(cnt_A=cnt_Bge cnt_C)(z)可以取到(cnt_B+cnt_C-cnt_A=cnt_C),以下证明(cnt_A-cnt_Cle L_B):变一下就是(cnt_A-L_B-L_Cle 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
",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("
");
	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("
");
			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;
}
	
原文地址:https://www.cnblogs.com/jz-597/p/13687993.html