挺棒的计数题。
先考虑给一个方案,如何判定它能否被构造出来。显然
′
w
′
'w'
′w′会把方案分成若干段,每段是独立的。对于某一段,如果只有
′
r
′
'r'
′r′,那么恰好需要一次
′
r
′
'r'
′r′操作,否则存在
′
b
′
'b'
′b′的话就需要先做一次
′
r
′
'r'
′r′操作,接着再做一次
′
b
′
'b'
′b′操作,接下来考虑段内的
′
b
′
'b'
′b′连续段数目为
d
d
d,那么我们显然需要再做至少
d
−
1
d-1
d−1次操作,事实上,这
d
−
1
d-1
d−1次操作是
′
r
′
'r'
′r′还是
′
b
′
'b'
′b′是无关紧要的,我们容易构造出一个方案(考虑其中的
′
r
′
'r'
′r′操作数目为
d
r
d_r
dr,我们第一个
′
b
′
'b'
′b′操作先反色
d
r
+
1
d_r+1
dr+1段
′
b
′
'b'
′b′连续段,中间会夹
d
r
d_r
dr段
′
r
′
'r'
′r′连续段,接下来
′
r
′
'r'
′r′操作就反色回去,
′
b
′
'b'
′b′操作就开新段)。这样的话,我们只需要知道段数和每段内的
‘
b
′
‘b'
‘b′连续段数目就可以判定了。
再考虑如何计数,我们枚举最终有
′
b
′
'b'
′b′的段数
p
p
p和仅含
′
r
′
'r'
′r′的段数
q
q
q。我们需要先给含
′
b
′
'b'
′b′的段分配
′
r
b
′
'rb'
′rb′,给仅含
′
r
′
'r'
′r′的段分配
′
r
′
'r'
′r′,如果不能分配显然不可行。如果可行的话我们会怎么分配呢?稍加思考的话我们可以证明最优的分配方式是尽可能的往前分配
p
p
p对
′
r
b
′
'rb'
′rb′,注意当遇到某个
′
b
′
'b'
′b′前面还有没分配的
′
r
′
'r'
′r′的时候也要分配最前的
′
r
′
'r'
′r′,然后再选择最前面没分配的
q
q
q个
′
r
′
'r'
′r′分配,这个容易用队列实现。这样分配好之后,我们考虑每个含
′
b
′
'b'
′b′的段中的
′
b
′
'b'
′b′连续段数目,我们显然会要求数目更多的分配到前面的
′
r
b
′
'rb'
′rb′,那么我们的限制可以变成
∀
i
\forall i
∀i,含
′
b
′
'b'
′b′的段中
′
b
′
'b'
′b′连续段数目最小的
i
i
i段总数不超过
u
p
i
up_i
upi。
对一组
(
p
,
q
)
(p,q)
(p,q)计数可以用一个DP实现,大概是设
F
[
i
]
[
j
]
[
k
]
F[i][j][k]
F[i][j][k]表示最小的
i
i
i段的
′
b
′
'b'
′b′连续段数目和为
j
j
j,最多的用了
k
k
k段,用一些简单的技巧转移可以做到单次DP
O
(
K
3
log
K
)
\mathcal O(K^3\log K)
O(K3logK)。DP出来后我们还要考虑每个含
′
b
′
'b'
′b′的段都可以在左右扩展一个
′
r
′
'r'
′r′连续段,这样不增加需要的操作次数。
最后合并答案用组合数计算即可。时间复杂度
O
(
K
5
log
K
)
\mathcal O(K^5\log K)
O(K5logK),不过常数非常小。题解的做法使用了整数拆分,在
K
=
70
K=70
K=70时跑的比这个多项式复杂度做法更快。。。
#include <bits/stdc++.h>
#define MOD 1000000007
using namespace std;
typedef long long ll;
inline void add(int &x,int y) {
((x+=y)>=MOD)?x-=MOD:0;
}
ll C[105][105];
void pre(int n) {
for(int i=0;i<=n;i++) C[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
}
int f[85][85][85],up[85];
int sum[85];
void dp(int n,int m,int c) {
memset(f,0,sizeof(f));
int t=m+2*c-1;
f[0][0][0]=1;
for(int i=0;i<m;i++)
for(int j=0;j<=up[i]&&2*j-m+t<=n;j++) {
int s=f[i][j][0];
for(int k=1;k<=up[m]&&j+(m-i)*k<=up[m];k++) {
for(int l=1;i+l<=m&&j+l*k<=up[i+l];l++)
add(f[i+l][j+l*k][k],s*C[i+l][l]%MOD);
add(s,f[i][j][k]);
}
}
for(int i=0;i<=up[m]&&2*i-m+t<=n;i++) {
int s=0;
for(int j=0;j<=i;j++) add(s,f[m][i][j]);
if (!s) continue;
int v=2*i-m+t;
for(int j=0;j<=2*m+2&&v+j<=n;j++)
add(sum[v+j],s*C[m+c][c]%MOD*C[2*m+2][j]%MOD);
}
}
char str[85];
int q[85],lpos[85];
bool vis[85];
int main() {
int n,k;
scanf("%d%d%s",&n,&k,str+1);
pre(100);
for(int i=0;2*i<=k;i++) {
memset(vis,0,sizeof(vis));
int cnt=0;
int lx=1,rx=0;
for(int j=1;j<=k&&cnt<i;j++)
if (str[j]=='r') q[++rx]=j;
else if (lx<=rx) {
vis[q[lx++]]=vis[j]=1;
lpos[++cnt]=j;
}
if (cnt<i) continue;
int s=0;
for(int j=1;j<=k;j++)
if (!vis[j]&&str[j]=='r') s++;
int r=1;
lpos[i+1]=k+1;up[i+1]=0;
for(int j=0;j<=s;j++) {
if (j) {
while (vis[r]||str[r]!='r') r++;
vis[r]=1;
}
for(int t1=i;t1>0;t1--) {
up[t1]=up[t1+1];
for(int t2=lpos[t1+1]-1;t2>lpos[t1];t2--)
if (!vis[t2]) up[t1]++;
up[t1]++;
}
reverse(up+1,up+i+1);
dp(n,i,j);
}
}
int ans=0;
for(int i=1;i<=n;i++) add(ans,sum[i]*C[n-1][i-1]%MOD);
printf("%d\n",ans);
return 0;
}