P2779 [AHOI2016初中組] 黑白序列題解

all_for_god發表於2024-11-13

題意:
小可可準備了一個未完成的黑白序列,用 BW 表示黑色和白色,用 ? 表示尚未確定。
他希望知道一共有多少種不同的方法,在決定了每一個 ? 位置的顏色後可以得到一個小雪喜歡的黑白序列。
其中,小雪喜歡的黑白序列指的是對於任何正整數 \(n\),由連續 \(n\) 個黑接上連續 \(n\) 個白的序列拼接而成的序列。

例如,如果用字元 BW 分別表示黑色,W 表示白色,那麼 BWBBWWBBBWWW 以及 BWBWBWBBWWBWBBWWBW 都是小雪喜歡的黑白序列。
WWWWBWBBW 以及 BBBWW 都不是小雪喜歡的黑白序列。

輸入長度不超過 \(500000\)

  • 此題資料本身很水,水到 \(n^2\) 都可以過,但還是要儘量寫正解

思路

  • 發現對於任意兩點 \(l,r\),如果其之間的這個序列是合法的,那麼

\(l\) 開始,至少有 \(\frac{r-l+1}{2}\) 個格子可以被塗成白色;從 \(r\) 開始向前,至少有 \(\frac{r-l+1}{2}\) 個格子可以被塗黑

  • 這麼看來,我們就可以將其分開維護,即預處理出向前向後最多能塗的黑白顏色,然後去維護區間和。
    由於只需要單點修改以及區間求和,因此樹狀陣列成了不錯的選擇。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+7;
const int p=1e9+9;
char s[N];int n,b[N],w[N],f[N],tr[N];
vector <int> ed[N];
int lowbit(int x){return x&(-x);}
void modify(int x,int val)
{
	x+=2;while(x<=n+2) tr[x]=(tr[x]+val)%p,x+=lowbit(x);
}
int query(int x)
{
	x+=2;int res=0;while(x) res=(res+tr[x])%p,x-=lowbit(x);//x+=2:small strick
	return res;
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>s+1;n=strlen(s+1);
	for(int i=1;i<=n;i++) if(s[i]=='B') w[i]=0;else w[i]=w[i-1]+1;
	for(int i=n;i>=0;i--) {if(s[i]=='W') b[i]=0;else b[i]=b[i+1]+1;ed[min(2*b[i+1]+i,n+1)].push_back(i);}
	f[0]=1;modify(0,1);
	for(int i=1;i<=n;i++)
	{
		if(s[i]!='B'&&i%2==0) f[i]=(query(i)-query(max(i-2*w[i]-2,-1ll))+p)%p,modify(i,f[i]);//注意這裡可能小於零,設為-1後在樹狀陣列中需要整體向右平移兩格
		int len=ed[i].size();for(int x=0;x<len;x++) modify(ed[i][x],-f[ed[i][x]]);
	}
	cout<<f[n]%p<<'\n';
	return 0;
}

相關文章