1. 程式人生 > 其它 >「CF1608D」Dominoes - 題解

「CF1608D」Dominoes - 題解

CF1608D 題解。
  • 分析

    首先,最後若干個骨牌一定滿足有 \(n\) 個黑色,\(n\) 個白色。

    先考慮不存在一個骨牌上兩個格子同一個顏色的情況(即保證每個骨牌都是一黑一白)。那麼最後的染色結果一定只有 左邊都是黑色 或者 右邊都是黑色 的 2 種情況。

    如果存在一個骨牌全是黑色,那麼也一定存在一個骨牌全是白色。

    我們發現,用這種全黑(或白)骨牌可以將左黑右白的骨牌與左白右黑的骨牌分開,使他們合法。

    也就是說,如果骨牌中存在全黑(白)骨牌,那麼剩下的顏色在滿足 \(n\) 個黑色 \(n\) 個白色的情況下,就可以隨意取。

    接下來考慮初始情況。設初始給了 \(s_w\) 個白色和 \(s_b\) 個黑色。

    如果初始情況存在一個全黑(白)骨牌,那麼答案即為 \(C_{2n-s_w-s_b}^{n-s_w}\)

    如果初始不存在,用上面那個組合數再減去不合法情況即可。

    即減去沒有染色出全黑(白)骨牌的所有情況,再加上 左邊都是黑色 或者 右邊都是黑色 的 2 種情況。注意一下這兩種情況是否會出現即可。


  • 程式碼
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int N=2e5+5;
const int Mod=998244353;
int n,ans,cnt,s1,s2,cnt1,cnt2,mul[N],inv[N];
char s[N][5];
int qpow(int x,int k)
{	int res=1;
	while(k)
	{	if(k&1)res=1ll*res*x%Mod;
		x=1ll*x*x%Mod;
		k>>=1;
	}
	return res;
}
int C(int x,int y){
	return 1ll*mul[y]*inv[x]%Mod*inv[y-x]%Mod;
}
int main()
{	scanf("%d",&n);
	mul[0]=inv[0]=1;
	for(int i=1;i<=(N-5);i++)mul[i]=1ll*mul[i-1]*i%Mod,inv[i]=qpow(mul[i],Mod-2);
	for(int i=1;i<=n;i++)
	{	scanf("%s",s[i]);
		cnt+=(s[i][0]!='?')+(s[i][1]!='?');
		s1+=(s[i][0]=='W')+(s[i][1]=='W');
		s2+=(s[i][0]=='B')+(s[i][1]=='B');
	}
//	printf("cnt:%d s1:%d s2:%d\n",cnt,s1,s2);
	for(int i=1;i<=n;i++)
	{	if(s[i][0]=='W'&&s[i][1]=='W')cnt1++;
		if(s[i][0]=='B'&&s[i][1]=='B')cnt2++;
	}
//	printf("C:%d %d\n",n-s1,2*n-cnt);
	if(cnt1||cnt2)printf("%d\n",C(n-s1,2*n-cnt));
	else{
		ans=C(n-s1,2*n-cnt);
		cnt1=cnt2=0;
		for(int i=1;i<=n;i++)
		{	if(s[i][0]=='W'||s[i][1]=='B')cnt1++;
			if(s[i][1]=='W'||s[i][0]=='B')cnt2++;
		}
//		printf("%d\n",ans);
//		printf("%d %d\n",cnt1,cnt2);
		if(!cnt1)ans++;
		if(!cnt2)ans++;
		ans=(1ll*ans+Mod-qpow(2,n-cnt1-cnt2))%Mod;
		printf("%d\n",ans);
	}
	return 0;
}