[AGC055B]ABC Supremacy

ABC Supremacy

题解

为什么有生物非要想用BIT维护这东西贡献了*发罚时。

我们有一种贪心地方法来构造出我们的目标队列。
首先,我们有一种方法能够让我们的可变的 A B C , B C A , C A B ABC,BCA,CAB ABC,BCA,CAB字母段整体移动,我们不妨设这样的一段为可变段。
假设我们的可变段与前一个字母的是 A x x x ‾ \overline {Axxx} Axxx,我们调整成 A B C A ‾ \overline {ABCA} ABCA,现在前三个有时可变段了,也就是 x x x A ‾ \overline{xxxA} xxxA
于是,如果现在的首位与我们目标的首位一样,我们就直接匹配。
如果现在的首位与我们目标的首位不一样,我们就将最靠前的可变段移动到前面去与之匹配。
匹配后就将首位后移,继续匹配下一位。

显然,我们可以移动用以匹配的段都是可变段,我们在不同的情况下也只能用这样的可变段去匹配。
而我们的可变段是不可能在序列中改变我们整个段的,该边的情况肯定是由于存在可变段。
如果有以下的情况 A B C A ‾ \overline{ABCA} ABCA出现了多个可变段,但它们实质上仍然只有一个,我们改变前面的后面的就会消失,改变后面的前面就会消失。
所以事实上我们本质不同的可匹配段数量是不变的,但在我们的匹配过程中其会因为我们已匹配部分的消失而造成可变段的减少与增加的假象,但纵观整个序列,可变段的数量是不变的。
这里需要注意的是,对于 A B A B C C ‾ \overline{ABABCC} ABABCC,我们认为它们实际上是两个可匹配段,因为我们将中间的 A B C ABC ABC变成 C A B CAB CAB,它就是可以分开的两个可变段了。
所以,我们上面做法的正确性是显然的。
但如果暴力像上面这样去做的话是 O ( n 2 ) O\left(n^2\right) O(n2)

但是如果我们实际上是没必要每一步都将我们的可变段暴力移动的,由于它是整段的交换位置,移动,我们可以用平衡树来维护我们的暴力,做到 O ( n log ⁡   n ) O\left(n\log\,n\right) O(nlogn),然后就罚时13发
但是我们已经有了上面的结论了,我们完全有更好的方法。
我们可以将所有的可变段都分离出来,当匹配到不行的时候再将可变段加在最前面。
如果最开头不是可变段且开头不能匹配我们就要从我们最开始分离下来的可变段中拿出一个去匹配。
显然当一个可变段开头匹配后,它后面的两位值就固定了,随着首位右移,该可变段消失。
容易发现,这样最多在首位后移时在最开头"增加"一个可变段,我们只需要在每次加入后再最开头是不是可变段就行了。
上面的方法显然可以用双端队列维护,时间复杂度 O ( n ) O\left(n\right) O(n)

但实际上还可以更简单。
我们最开始已经证明了广义的可变段数量是不变的,而可变段不可能影响其余部分的相对顺序与值。
所以我们可以将字符串 S S S和字符串 T T T中所有的可变段都分离出来,比较剩下的段是否相同就可以了。
根本不需要什么平衡树。

时间复杂度 O ( n ) O\left(n\right) O(n)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;     
const int INF=0x3f3f3f3f;  
const int inv2=499122177;
const int jzm=2333;
const int n1=50;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){putchar('\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,cnt,lens,lent,a[MAXN],b[MAXN],q[MAXN],head,tail;
char strs[MAXN],strt[MAXN]; 
bool check(){
	if(tail-head<3)return 0;
	if((q[tail-3]+1)%3==q[tail-2]&&(q[tail-3]+2)%3==q[tail-1])
		return (tail-=3,1);
	return 0;
}
signed main(){
	read(n);scanf("\n%s\n%s",strs+1,strt+1);
	for(int i=1;i<=n;i++){q[tail++]=strs[i]-'A';while(check());}
	for(int i=head;i<tail;i++)a[++lens]=q[i];head=tail=0;
	for(int i=1;i<=n;i++){q[tail++]=strt[i]-'A';while(check());}
	for(int i=head;i<tail;i++)b[++lent]=q[i];head=tail=0;
	if(lens^lent){puts("NO");return 0;}
	for(int i=1;i<=lens;i++)if(a[i]^b[i]){puts("NO");return 0;}
	puts("YES");
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值