题解「Japan Alumni Group Summer Camp 2018 Day 2J AB Sort」
题意
对字符串 \(s\) 进行一次操作,使得 \(s\) 中所有为 \(\text{BA}\) 的子串同时变为 \(\text{AB}\) 。定义 \(\mathrm{f}(s)\) 表示不断对 \(s\) 进行上述操作,直到无法再操作为止,最多能对 \(s\) 进行的操作次数。
给定字符串 \(s\) ,要求支持区间 \(\text{AB}\) 翻转,查询 \(\mathrm{f}('\mathrm{B}'+s+'\mathrm{A}')\) 。
题解
发现进行这样的操作的本质是对 \(s\) 排序,最后所有的 \(\text{A}\) 都在 \(\text{B}\) 的前面。也就是说,只要存在一个 \(\text{B}\) 在 \(\text{A}\) 前面,那么这个字符串就还能被操作。发现所有 \(\text{B}\) 的相对位置是不变的,所以只需要考虑最左边的那个 \(\text{B}\) 被移动到正确的位置上的操作次数。
显然,如果每次操作都让最左边的 \(\text{B}\) 移动了一位,那么总操作次数即为 \(\mathrm{cnt}(\text{A})+1\) (这里 \(\mathrm{cnt}\) 计算的是 \(s\) 中字母的个数,不包括两段加入的字符),但是并不是每次操作都会移动最左边的 \(\text{B}\) 。不难发现,本次操作不会移动某个位置的 \(\text{B}\) ,当且仅当它后面紧跟着一个 \(\text{B}\) ,而不是 \(\text{A}\)(后面没有字符除外),我们将这种情况称作这个 \(\text{B}\) 被后面的 \(\text{B}\) 阻挡。
考虑下面这种情况(假设最后一段 \(\text{A}\) 足够长):
若 \(c_1\geq c_2\) ,那么当最左边的 \(\text{B}\) 移动到第一段 \(\text{A}\) 之后时,长度为 \(c_2\) 的那段 \(\text{B}\) 已经全部移动,即此时最左边的 \(\text{B}\) 后面紧接着的是一个 \(\text{A}\) ,那么它就可以继续向后移动,换而言之,这段长度为 \(c_2\) 的 \(\text{B}\) 对最左边的 \(\text{B}\) 的移动无影响。
否则 \(c_1<c_2\) ,最左边的 \(B\) 移动到 \(\text{A}\) 之后时,后面那段 \(\text{B}\) 还有 \(c_2-c_1\) 个没有移动,即它后面紧接着有 \(c_2-c_1\) 个 \(\text{B}\) ,再进行操作时这个 \(\text{B}\) 就无法移动了,它再次移动还需要 \(c_2-c_1\) 次操作。
如果最后一段 \(\text{A}\) 不够长,即下面一种情况:
其中 \(c_3<c_4\) ,那么 \(c_2\) 这一段就不能顺利地走过 \(c_3\) 这一段了,它会被后面 \(c_4\) 这一段阻挡 \(c_4-c_3\) 次操作,那么最左边的 \(\text{B}\) 就会被阻挡 \(c_4+c_2-c_1-c_3\) 次操作。
到这里就能大概看出结论了:若将 \(\text{A}\) 看成 \(-1\) ,\(\text{B}\) 看成 \(1\) ,序列的最大前缀和为 \(\mathrm{lmax}(s)\) ,答案即为 \(\mathrm{cnt}(\text{A})+1+\mathrm{lmax}(s)\) 。
用线段树维护即可。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <climits>
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const int maxn=2e5+5;
template <typename T>
inline void read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
x*=f;
}
int n,q;
char s[maxn];
namespace Segment_Tree
{
struct node
{
int l,r,cntA,cntB;
int sum,lmin,lmax;
bool tag;
node(int l,int r,int cntA,int cntB,int sum,int lmin=0,int lmax=0,bool tag=false)
:l(l),r(r),cntA(cntA),cntB(cntB),sum(sum),lmin(lmin),lmax(lmax),tag(tag){}
node(){}
inline node operator + (const node &T)const
{
node res(l,T.r,cntA+T.cntA,cntB+T.cntB,sum+T.sum);
res.lmin=min(lmin,sum+T.lmin);
res.lmax=max(lmax,sum+T.lmax);
return res;
}
}tree[maxn<<2];
#define ls (p<<1)
#define rs (p<<1|1)
inline void reverse(int p)
{
if(!p) return;
swap(tree[p].cntA,tree[p].cntB);
swap(tree[p].lmin,tree[p].lmax);
tree[p].lmin=-tree[p].lmin;
tree[p].lmax=-tree[p].lmax;
tree[p].sum=-tree[p].sum;
tree[p].tag^=1;
}
inline void push_down(int p)
{
if(!tree[p].tag) return;
reverse(ls);
reverse(rs);
tree[p].tag=0;
}
inline void set(int p,char c)
{
tree[p].sum=c=='A'?-1:1;
c=='A'?(tree[p].cntA=1):(tree[p].cntB=1);
tree[p].lmin=min(0,tree[p].sum);
tree[p].lmax=max(0,tree[p].sum);
}
void build(int p,int l,int r)
{
tree[p]=node(l,r,0,0,0);
if(l==r) return set(p,s[l]),void();
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
tree[p]=tree[ls]+tree[rs];
}
void modify(int p,int L,int R)
{
int l=tree[p].l,r=tree[p].r;
if(L<=l&&r<=R) return reverse(p),void();
push_down(p);
int mid=(l+r)>>1;
if(L<=mid) modify(ls,L,R);
if(R>mid) modify(rs,L,R);
tree[p]=tree[ls]+tree[rs];
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("P100137.in","r",stdin);
#endif
read(n);
scanf(" %s\n",s+1);
Segment_Tree::build(1,1,n);
read(q);
int l,r;
while(q--)
{
read(l),read(r);++l,++r;
Segment_Tree::modify(1,l,r);
printf("%d\n",Segment_Tree::tree[1].cntA+Segment_Tree::tree[1].lmax+1);
}
return 0;
}