题目大意
题解
半想半看myh的做法
官方题解是暴力枚举划分然后算,因为n和k很小所以跑得很快
首先有一个结论:一段有d个连续b段的染色结果是由rb+(d-1)个r或b所造成的
证明考虑归纳,首先d=1的时候成立,然后每加上一个b就把第一段b去掉,剩下的是d-1的情况;如哦加上一个r就考虑把第一段的b一分为二,根据归纳也可以保证第一个出现的b在最左边
然后可以考虑dp,先枚举rb的段数x和r的段数y
一开始想的是n^6log的做法,即设f[i,j,k]表示放了i段总和为j当前最短的一段为k,转移枚举长度以及个数(枚举个数是nlogn的)
后来发现不用枚举长度,先假设放最短的然后枚举扩展的长度再放进去(brb可以往两边加r),最后直接组合数即可n^5log
然后100个点里挂了3个,原因是从大往小放的话放完一个段之后可能要去掉一些不选的,如果加一维就是n^6log
实际上直接从小往大放即可n^5log
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define add(a,b) a=((a)+(b))%1000000007
#define min(a,b) (a<b?a:b)
#define mod 1000000007
#define Mod 1000000005
#define ll long long
#define N 1000
//#define file
using namespace std;
ll f[71][71][72],jc[N+1],Jc[N+1],ans;
int d[71],n,m,i,j,k,l,x,y;
char a[71];
bool bz[71];
ll qpower(ll a,int b) {ll ans=1; while (b) {if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans;};
ll C(int n,int m) {if (n<m) return 0;return jc[n]*Jc[m]%mod*Jc[n-m]%mod;}
int main()
{
#ifdef file
freopen("arc089F.in","r",stdin);
// freopen("a.out","w",stdout);
#endif
jc[0]=1;
fo(i,1,N) jc[i]=jc[i-1]*i%mod;
Jc[N]=qpower(jc[N],Mod);
fd(i,N-1,0) Jc[i]=Jc[i+1]*(i+1)%mod;
scanf("%d%d",&n,&m);
scanf("%s",a+1);
ans=1;
fo(x,0,n)
{
fo(y,0,n)
if (x || y)
{
memset(bz,0,sizeof(bz)),k=l=0;
fo(i,1,x)
{
fo(j,1,m) if (!bz[j] && a[j]=='r') break;
fo(k,j,m) if (!bz[k] && a[k]=='b') break;
if (k>m) break;
bz[j]=bz[k]=1;
}
if (k>m) continue;
fo(i,1,m) if (l<y && !bz[i] && a[i]=='r') ++l,bz[i]=1;
if (l<y) continue;
l=0;
fd(j,m,1)
if (bz[j] && a[j]=='b')
{
d[++l]=0;
fo(k,j,m) d[l]+=!bz[k];
}
memset(f,0,sizeof(f)),f[0][0][0]=1;
fo(i,0,x-1)
{
fo(j,0,d[x])
{
fo(k,0,d[x])
if (f[i][j][k])
{
if (k<d[x]) add(f[i][j][k+1],f[i][j][k]);
fo(l,1,x-i)
if (j+l*k<=d[i+l])
add(f[i+l][j+l*k][k+1],f[i][j][k]*C(i+l+y,l));
else break;
}
}
}
fo(j,0,d[x])
{
fo(k,0,d[x]+1)
{
fo(l,0,n)
add(ans,f[x][j][k]*C(l+(y+2*j+x*3)-1,(y+2*j+x*3)-1)%mod*C(n-(x+y-1)-2*j-l,x+y));
}
}
}
}
printf("%lld
",ans);
fclose(stdin);
fclose(stdout);
return 0;
}