題面
題意
給出一個環,上面有n個點,將他劃分爲n段弧,現在要求對每段弧進行紅藍染色,要求染色完成後,從每個點開始通過在環上移動,經過的弧構成的字符串都可以成爲給定的字符串,問一共有幾種染色方法.
做法
首先要確定一些結論.
假設給出字符串的第一個字符爲’R’('B’同理).
分兩種情況進行討論:
1.給定字符串中的所有字符都相同
可以發現環上不存在兩個連續的弧且它們的顏色都是B,因此除了所有弧同色的情況外,可以將圓上的弧看作形如RR…RRRB的多段,每段長度至少爲2,這樣題目就轉化爲了將n段弧分成多段,每段長度都大於1,有幾種填法,答案統計方法與下一種情況相同.
2.給定字符串中的所有字符有R也有B
同樣可以發現環上不存在兩個連續的弧且它們的顏色都是B,而且若給定字符串的第一段連續的R的長度爲l,則可以發現環上最長的連續的R弧的長度不超過%,記%,若字符串中間(不能是最後)存在一段長度爲l的R且l爲奇數,則可以發現環上最長的連續的R弧的長度不超過,.
這樣就可以將圓上的弧看作形如RR…RRRB的多段,其中R的長度爲不大於的奇數,將它與B看作一組,這樣不難發現n爲奇數時無解,題目就轉化爲了將段弧分成多段,每段長度都不大於的方案數.
爲了方便表達,令
首先考慮在線段上怎麼做這道題,記表示i段線段的答案,這樣,這個顯然可以用前綴和優化,然後考慮如何在環上做這個問題.
我們可以暴力枚舉第一個點所在的那一段的長度爲多少,這樣環上除這一段外剩下的部分就是一條線段,可以用dp解決,然後累加即可得到答案.
代碼
#include<bits/stdc++.h>
#define ll long long
#define N 200100
#define M 1000000007
using namespace std;
ll n,m,mx,ans,dp[N],qz[N];
char str[N];
inline void Add(ll &u,ll v){u=(u+v)%M;}
int main()
{
ll i,j,t;
cin>>n>>m;
scanf("%s",str+1);
for(i=1;i<=m;i++) if(str[i]!=str[1]) break;
mx=i-1;
if(mx==m)
{
dp[0]=qz[0]=ans=1;
for(i=0;i<=n;i++)
{
if(i>=2) dp[i]=qz[i-2];
qz[i]=(qz[i-1]+dp[i])%M;
if(n-i>=2) Add(ans,dp[i]*(n-i)%M);
}
cout<<ans;
return 0;
}
if(n&1)
{
puts("0");
return 0;
}
if(mx%2==0) mx++;
for(t=0;i<=m;i++)
{
if(str[i]==str[1]) t++;
else
{
if(t&1) mx=min(mx,t);
t=0;
}
}
n/=2,mx=(mx+1)/2;
dp[0]=qz[0]=1;
for(i=0;i<=n;i++)
{
if(i)
{
dp[i]=(M+qz[i-1]-(i-mx-1>=0?qz[i-mx-1]:0))%M;
qz[i]=(qz[i-1]+dp[i])%M;
}
if(n-i<=mx) Add(ans,(n-i)*dp[i]*2%M);
}
cout<<ans;
}