洛谷-P9574 題解

IOIAK_wanguan發表於2024-08-05

思路分析

分析樣例:

== TTBT BTTBTB TTT BTTB
-> TTBT TBBTTB TTT BTTB
-> TTBT TBTBBT TTT BTTB
-> TTBT TBTBBT TTT TBBT
----1-----2-----3---4--

觀察區塊 2,發現 BTTB 進行操作後與右邊的 TB 再次構成 BTTB,我們發現在這個區塊內,可以從左向右不斷操作,我們稱這種特性為傳遞性,可以發現其具有方向。

假設區塊 2 右邊有更多的 TB,例如 BTTBTBTBTBTB,是否仍然存在傳遞性呢?沒錯,你可以在紙上試一下,答案肯定。

推論 1:具有 BTTB TBTB... 特徵的區塊中,可以向右不斷操作,操作具有傳遞性,方向向右。

那我們再反過來看呢?難道就不能向左不斷操作嗎?

我們在區塊 2 的左邊加上一些 BT,可以發現可以不斷向左傳遞。

推論 1.1:BTTB TBTB... 具有向右的傳遞性;...BTBT BTTB 具有向左的傳遞性。

綜合一下:

推論 1.2:...BTBT BTTB TBTB... 具有雙向的傳遞性,在它的右方傳遞性向右,左方則向左。

考慮極限思維,...BTBT BTTB TBTB... 捨去了它的尾巴,變成 BTTB。思考發現這也是具有雙向傳遞性的,只不過只能連續操作 1 次。

推論 1.3:BTTB 也具有雙向傳遞性。

那麼,這有什麼用呢?

再次觀察樣例(如上),可以發現透過傳遞性進行操作,區塊 3 左邊多加了一個 T,右邊也多加了一個 T。是的,我們可以透過區塊的傳遞性對某個連續 T 區間增加 T

我們對推論 1.1 再次分析,對於區間 BTTB TBTB...,可以發現右邊的 B 變成了 T;而對於區間 ...BTBT BTTB,左邊的 B 變成了 T

由於區塊 3 在區塊 2 右邊,區塊 2 表現出向右的傳遞性,透過操作區塊 2 右邊的 B 變成 T,由於兩個區塊相鄰,區塊 3 連續 T 的長度增加 1。同理區塊 4 表現出向左的傳遞性,鄰接於區塊 3,使得區塊 3 連續 T 在右邊又增加了 1。

推論 2:對於一個具有傳遞性的區塊,它可以使它表現出的傳遞性方向上的鄰接連續 T 區塊長度增加 1。

可以發現,當某個具有傳遞性的區塊進行操作後,它將損失其傳遞性,不再可操作,不再能給鄰接 T 區塊提供新的 T。也就是說:

推論 3:一個具有傳遞性的區塊只能對其表現出的傳遞性方向上的鄰接連續 T 區塊貢獻一次。

綜合推論 2 和推論 3,我們可以得出推論 4:一個連續 T 區塊只能收到兩次貢獻,來自左和右的兩個方向,也就是說,每個連續 T 區間長度最多增加 2,其增加的 T 分別來源於左邊和右邊兩個方向的與其方向相反的連續性區塊。

從推論 4 的視角,我們再來看一次樣例,我們把表現出向右連續性的區塊視為 ->,向左的視為 <-

TTBT -> TTT <-

可以看出連續性的方向對答案是存在影響的。

如此,我們找到每個具有連續性的區塊,區塊左邊表現出向左的連續性,並打上標記,向右也是如此,但注意要區分方向。

然後列舉一遍連續 T 區塊,如果其某個邊界處存在標記且該標記方向正確,則該方向使長度加 1,最後取所有區塊這樣執行後的長度的最大值即可。

蒟蒻已經盡力說清楚了 QAQ。

程式碼實現

#define by_wanguan
#include<iostream>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=5e5+7;
int t,n,le[N],ri[N],ans; char c[N];
int main(){
  ios::sync_with_stdio(false),cin.tie(0);
  cin>>t; while(t--){
  cin>>n; for(int i=2;i<=n+1;i++) cin>>c[i],le[i]=ri[i]=0;
  int l=1,r=l+3; ans=0; c[0]=c[1]=c[n+2]=c[n+3]='#';
  le[0]=le[1]=le[n+2]=le[n+3]=ri[0]=ri[1]=ri[n+2]=ri[n+3]=0;//初始化
  while(r<=n){//透過雙指標查詢有連續性的區間
    l++,r++;
    if(c[l]=='B'&&c[l+1]=='T'&&c[l+2]=='T'&&c[l+3]=='B'){
	  ri[r]++,le[l]++;//發現存在,則在兩端打上標記,le的標記方向向左,ri向右
      while(c[r+1]=='T'&&c[r+2]=='B') r+=2,ri[r]++;//向左右擴充套件區塊並打上標記(找尾巴)
      while(c[l-1]=='T'&&c[l-2]=='B') l-=2,le[l]++;
      l=r-1,r=l+3;//l跳到r的位置,在本次操作後l和r都會++,提前減1
    }
  }
  l=1,r=l;
  while(r<=n){
    l=r,l++,r++;
    if(c[l]!='T') continue;//查詢連續T區間
    while(c[r+1]=='T') r++;//擴充套件連續T區間
    ans=max(r-l+1+ri[l-1]+le[r+1],ans);//查詢是否存在標記,注意方向
  }
  cout<<ans<<'\n';
}
}

有點 DP 的味道,看不懂請狠狠踢我!蒟蒻會盡量解答的。

喵。

相關文章