題解「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}\) 移動了一位,那麼總操作次數即為 \(\mathrm{cnt}(\text{A})+1\) (這裡 \(\mathrm{cnt}\) 計算的是 \(s\) 中字母的個數,不包括兩段加入的字元),但是並不是每次操作都會移動最左邊的 \(\text{B}\) 。不難發現,本次操作不會移動某個位置的 \(\text{B}\)
考慮下面這種情況(假設最後一段 \(\text{A}\) 足夠長):
\[\text{B}~\overbrace{\text{AA}\ldots\text{AA}}^{c_1}~\overbrace{\text{BB}\ldots\text{BB}}^{c_2}~\text{AA}\ldots \]若 \(c_1\geq c_2\) ,那麼當最左邊的 \(\text{B}\) 移動到第一段 \(\text{A}\)
否則 \(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}\) 不夠長,即下面一種情況:
\[\text{B}~\overbrace{\text{AA}\ldots\text{AA}}^{c_1}~\overbrace{\text{BB}\ldots\text{BB}}^{c_2}~\overbrace{\text{AA}\ldots\text{AA}}^{c_3}~\overbrace{\text{BB}\ldots\text{BB}}^{c_4}~\text{AA}\ldots \]其中 \(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;
}