题面
题意
给出一个环,上面有n个点,将他划分为n段弧,现在要求对每段弧进行红蓝染色,要求染色完成后,从每个点开始通过在环上移动,经过的弧构成的字符串都可以成为给定的字符串,问一共有几种染色方法.
做法
首先要确定一些结论.
假设给出字符串的第一个字符为’R’('B’同理).
分两种情况进行讨论:
1.给定字符串中的所有字符都相同
可以发现环上不存在两个连续的弧且它们的颜色都是B,因此除了所有弧同色的情况外,可以将圆上的弧看作形如RR…RRRB的多段,每段长度至少为2,这样题目就转化为了将n段弧分成多段,每段长度都大于1,有几种填法,答案统计方法与下一种情况相同.
2.给定字符串中的所有字符有R也有B
同样可以发现环上不存在两个连续的弧且它们的颜色都是B,而且若给定字符串的第一段连续的R的长度为l,则可以发现环上最长的连续的R弧的长度不超过
l
+
l
l+l
l+l%
2
2
2,记
m
x
=
l
+
l
mx=l+l
mx=l+l%
2
2
2,若字符串中间(不能是最后)存在一段长度为l的R且l为奇数,则可以发现环上最长的连续的R弧的长度不超过
l
l
l,
m
x
=
min
(
m
x
,
l
)
mx=\min(mx,l)
mx=min(mx,l).
这样就可以将圆上的弧看作形如RR…RRRB的多段,其中R的长度为不大于
m
x
mx
mx的奇数,将它与B看作一组,这样不难发现n为奇数时无解,题目就转化为了将
n
/
2
n/2
n/2段弧分成多段,每段长度都不大于
(
m
x
+
1
)
/
2
(mx+1)/2
(mx+1)/2的方案数.
为了方便表达,令
n
=
n
/
2
,
m
x
=
(
m
x
+
1
)
/
2
n=n/2,mx=(mx+1)/2
n=n/2,mx=(mx+1)/2
首先考虑在线段上怎么做这道题,记
d
p
[
i
]
dp[i]
dp[i]表示i段线段的答案,这样
d
p
[
x
]
=
∑
i
=
m
a
x
(
0
,
i
−
m
x
)
i
−
1
d
p
[
i
]
dp[x]=\sum_{i=max(0,i-mx)}^{i-1}dp[i]
dp[x]=∑i=max(0,i−mx)i−1dp[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;
}