Codeforces Round #758 (Div.1 + Div. 2) D. Dominoes题解

 

 一道脑筋急转弯的构造题

对于重排类型的问题,一定要注意对数据分类型进行统计

题意很简单,就不解释了

首先,我们假设已经涂好了所有的多米诺骨牌

那么所有的骨牌可以分为4类:BB、WW、BW、WB

由于上一个骨牌的右方块的颜色与下一个骨牌的左方块颜色不同

所以,黑色与白色的个数都等于n

然后我们发现BW BW BW……可以一直往下连,WB WB WB……也可以一直往下连

至此BW与WB的构造问题就解决了(排成一堆即可)

如果存在BB或WW,那么它们一定会成对出现(即有一个BB必有一个WW)

那么我们可以这样构造

WW
BB
WW
BB
......(多余的“WW BB”全部排一起)

WW (单独拿一对WW和BB)

BW
...... (所有BW排一堆)
BW

BB

WB
...... (所有WB排一堆)
WB

至此,我们就完成了所有多米诺骨牌的构造

(当然,这只是众多构造方式中的一种)

接下来我们考虑计数

我们利用容斥,使所有W和B的数量相等的涂色方案总数为:C(2*n-W-B,n-W)

减去不合法的情况:当所有骨牌都是BW或者WB(但不全是BW或不全是WB)的情况

问题就解决了

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005
char a[N][4];
int fac[2*N],invfac[2*N];
const int mod=998244353;
int ksm(int x,int y)
{
	int ret=1;
	while(y){
		if(y&1)ret=1ll*ret*x%mod;
		y>>=1;x=1ll*x*x%mod;
	}
	return ret;
}
int C(int n,int m)
{
	return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
int main()
{
	int n,i,ans,W=0,B=0;
	scanf("%d",&n);
	invfac[0]=fac[0]=1;
	for(i=1;i<=2*n;i++)
		fac[i]=1ll*fac[i-1]*i%mod;
	invfac[2*n]=ksm(fac[2*n],mod-2);
	for(i=2*n-1;i>=1;i--)
		invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
	for(i=1;i<=n;i++){
		scanf("%s",a[i]);
		W+=(a[i][0]=='W')+(a[i][1]=='W');
		B+=(a[i][0]=='B')+(a[i][1]=='B');
	}
	if(W>n||B>n){
		printf("0\n");
		return 0;
	}
	ans=C(2*n-W-B,n-W);
	int flg=0;
	for(i=1;i<=n;i++)
		if(a[i][0]==a[i][1]&&a[i][0]!='?'){
			flg=1;
			break;
		}
	if(flg==0){
		// subtract the circumistances that every domino is (WB or BW)
		int sub=1;
		for(i=1;i<=n;i++)
			if(a[i][0]=='?'&&a[i][1]=='?')
				sub=2ll*sub%mod;
		ans=(1ll*ans-sub+mod)%mod;
	}
	
	// however, if (every domino is WB) or (every domino is BW) , the circumistance is vaild
	int flgbw=1,flgwb=1;
	for(i=1;i<=n;i++){
		if(a[i][0]=='B'||a[i][1]=='W')
			flgwb=0;
		if(a[i][0]=='W'||a[i][1]=='B')
			flgbw=0;
	}
	ans=(ans+flgbw+flgwb)%mod;
	printf("%d\n",ans);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值