一道脑筋急转弯的构造题
对于重排类型的问题,一定要注意对数据分类型进行统计
题意很简单,就不解释了
首先,我们假设已经涂好了所有的多米诺骨牌
那么所有的骨牌可以分为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);
}