1. 程式人生 > 實用技巧 >題解「Japan Alumni Group Summer Camp 2018 Day 2J AB Sort」

題解「Japan Alumni Group Summer Camp 2018 Day 2J AB Sort」

GMOJ100137 胖頭魚的排序

題意

對字串 \(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}\) 足夠長):

\[\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_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}\) 不夠長,即下面一種情況:

\[\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;
}