題意:
小可可準備了一個未完成的黑白序列,用 B
和 W
表示黑色和白色,用 ?
表示尚未確定。
他希望知道一共有多少種不同的方法,在決定了每一個 ?
位置的顏色後可以得到一個小雪喜歡的黑白序列。
其中,小雪喜歡的黑白序列指的是對於任何正整數 \(n\),由連續 \(n\) 個黑接上連續 \(n\) 個白的序列拼接而成的序列。
例如,如果用字元 B
和 W
分別表示黑色,W
表示白色,那麼 BW
,BBWW
,BBBWWW
以及 BWBW
,BWBBWW
,BWBBWWBW
都是小雪喜歡的黑白序列。
而 W
,WW
,WB
,WBBW
以及 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;
}