ARC089F ColoringBalls

ARC089F

n n n个排成一行的白色(w)小球。给你个操作序列,r表示将一段区间染成红色,b表示将一段区间染成蓝色(其中这个区间不能有白色的球)。

区间可以为空。

问操作完之后,可能得到的不同的小球颜色序列的个数。

n ≤ 70 n\le 70 n70


被ARC打倒了。。。

先考虑如何判定是否有可行解:

首先把字符串unique,然后以w为分界点分成若干段。

对于某一段,假如它有 x x x b b b,那么它便可以以以下操作完成:

  1. x = 0 x=0 x=0。操作:r
  2. x > 0 x>0 x>0。操作:r+b+?*(x-1)?表示任意操作。

(这里的+*分别表示拼接和重复)

证明考虑构造,对于b出现了 x x x次的区间,可以通过 x x x b b b操作完成;然后对于一个brb的子区间,可以先染成bbb,再在中间点缀个r变成brb。这就相当于是将一个b操作替换成r操作。

对于这些段我们得到了若干个操作序列,如果我们能再题目给的操作序列中找到这些操作序列并且它们不相交,那么这个就是合法的。

贪心找操作序列:优先考虑rb开头的子序列(先只配对rb,后面一堆?先不管)。从左往右扫,用个队列将沿途遇到的r记下来,如果遇到了一个b,就拿最前面的r跟它配对。这样如果配对完了所有rb,就在队列中优先从前往后配r。最后就是补?,将rb后面?的需求挂在b的位置上,然后从后往前扫,遇到有需求的位置就直接给它补充空位,如果不够空位就不行。

我们将若干个 x + 1 x+1 x+1合起来变成一个可重集。本质不同的可重集的个数不会太多。

n = 70 n=70 n=70时,个数为 418662 418662 418662

用于检验自己枚举是否出问题。

暴力枚举这些可重集并判断其是否合法。最后的问题是计数。

首先答案成上可重集的排列方案数。

先考虑unique之后的方案数。

x = 1 x=1 x=1时,必须有个r x > 1 x>1 x>1时,至少有形如brbrb的段,长度为 2 x − 3 2x-3 2x3。然后还要记上两端之间必须要有个w。于是我们得到必要的对应方案的子序列。

然后在考虑可有可无的:在 x > 1 x>1 x>1的一段的两边,可以一个 r r r;还有对于最左端和最右端可以插w

于是我们得到了一个更大的序列,其中有些字符必须存在,有些可能不存在。

unique去掉,那么其中的每个字符都可以被一段连续的相同区间替代。其中有些区间可以为空,有些区间不能为空。

变成了个简单的组合问题:有若干个箱子,有些箱子可以为空有些不能,问将 n n n个球塞进去的方案数。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 75
#define ll long long
#define mo 1000000007
ll qpow(ll x,ll y=mo-2){
    
    
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
ll fac[N*4],ifac[N*4];
void initC(int n){
    
    
	fac[0]=1;
	for (int i=1;i<=n;++i)
		fac[i]=fac[i-1]*i%mo;
	ifac[n]=qpow(fac[n]);
	for (int i=n-1;i>=0;--i)
		ifac[i]=ifac[i+1]*(i+1)%mo;
}
ll inv(int n){
    
    return ifac[n]*fac[n-1]%mo;}
ll C(int m,int n){
    
    
	if (m<n) return 0;
	return fac[m]*ifac[n]%mo*ifac[m-n]%mo;
}
int n,K;
char str[N];
int p[N],m;
ll ans;
bool judge(){
    
    
	static int q[N],need[N];
	static int used[N];
	int head=1,tail=0;
	memset(used,255,sizeof(int)*(K+1));
	int i=1,j=1;
	for (;j<=K && i<=m && p[i]>1;++j){
    
    
		if (head<=tail && str[j]=='b')
			used[j]=p[i++]-2,used[q[head++]]=0;
		else if (str[j]=='r')
			q[++tail]=j;
	}
	if (i<=m && p[i]>1) return 0;
	for (;j<=K;++j)
		if (str[j]=='r')
			q[++tail]=j;
	for (;i<=m;++i){
    
    
		if (head>tail) return 0;
		used[q[head++]]=0;
	}
	int cnt=0;
	for (int j=K;j>=1;--j){
    
    
		if (used[j]==-1)
			cnt++;
		else if (used[j]>0){
    
    
			if (cnt<used[j]) return 0;
			cnt-=used[j];
		}
	}
	return 1;
}
ll calc(){
    
    
//	for (int i=1;i<=m;++i)
//		printf("%d ",p[i]);
//	printf("\n");
	if (m==0) return 1;
	p[m+1]=-1;
	ll s=fac[m];
	for (int i=1,j=0;i<=m;++i){
    
    
		j++;
		if (p[i]!=p[i+1]){
    
    
			s=s*ifac[j]%mo;
			j=0;
		}
	}
	ll c0=2,c1=m-1;
	for (int i=1;i<=m;++i){
    
    
		if (p[i]==1)
			c1++;
		else{
    
    
			c1+=2*p[i]-3;
			c0+=2;
		}
	}
	s=s*C((n-c1)+(c1+c0)-1,n-c1)%mo;
//	printf("ans=%lld\n",s);
	return s;
}
void dfs(int s,int k){
    
    
	if (judge())
		ans+=calc();
	if (s==0)
		return;
	for (int i=k;i>=1;--i){
    
    
		if (s>=2*i-1+(s!=n)){
    
    
			p[++m]=i+1;
			dfs(s-(2*i-1)-(s!=n),i);
			--m;
		}
	}
	if (s>=1+(s!=n)){
    
    
		p[++m]=1;
		dfs(s-1-(s!=n),0);
		--m;
	}
}
int main(){
    
    
//	freopen("in.txt","r",stdin);
	scanf("%d%d%s",&n,&K,str+1);
	initC(n*4);
	dfs(n,n);
	ans%=mo;
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/A1847225889/article/details/108784530