首先考虑怎么判断一堆牌里是否有个子集可以胡,首先7张对子比较好判,直接看个数(ge 2)的牌的总数,但是面子似乎不好贪心,考虑dp,设(f_{i,0/1,j,k})表示只有前(i)种牌的集合,是否有对子,以后要凑(j)个(i-1,i,i+1)以及(k)个(i,i+1,i+2),最多会有多少个面子.注意这里(j,k)是(le 2)的,不然就可以搞出三个刻子,转移枚举(i+1)放多少张,然后枚举放多少个(i+1,i+2,i+3),然后枚举是否凑出一个对子,剩下的牌全部用来凑刻子进行转移
然后如果一副牌胡了,也就是对子(ge 7),或者面子(ge 4)还有对子,那么就不能转移的,所以考虑搜出所有不能胡的集合,然后要把重复的状态判掉,发现这样所有的不胡的状态只有(2091)个
刚才搜状态的时候,可以发现这些状态构成了一个自动机,可以判断一副牌是否胡了.题目要求的是胡牌要摸多少牌的期望,根据期望的线性性可以看成摸了(i)张牌没有胡的概率之和.所以设(g_{i,j,k}),表示前(i)种牌,现在在自动机上的第(j)位,总共摸了(k)张牌的方案,转移枚举下一位放多少张,沿着自动机转移,还要乘上一个转移系数(就是组合数),最后算出的方案/总方案就是答案
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
#define db double
using namespace std;
const int N=100+10,M=2100+10,mod=998244353;
int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
struct node
{
int mz[2][3][3],dz;
node(){memset(mz,-1,sizeof(mz)),dz=0;}
bool operator < (const node &bb) const
{
for(int i=0;i<=1;++i)
for(int j=0;j<=2;++j)
for(int k=0;k<=2;++k)
if(mz[i][j][k]!=bb.mz[i][j][k]) return mz[i][j][k]<bb.mz[i][j][k];
return dz<bb.dz;
}
}emp;
map<node,int> mp;
int tt,go[M][5],n,cn[N],fac[N<<2],iac[N<<2],f[2][M][N<<2];
int fpow(int a,int b){int an=1;while(b){if(b&1) an=1ll*an*a%mod;a=1ll*a*a%mod,b>>=1;} return an;}
int C(int n,int m){return m<0||n<m?0:1ll*fac[n]*iac[m]%mod*iac[n-m]%mod;}
bool hu(node x)
{
if(x.dz>=7) return 1;
for(int j=0;j<=2;++j)
for(int k=0;k<=2;++k)
if(x.mz[1][j][k]>=4) return 1;
return 0;
}
node trans(node x,int nm)
{
node nw;
nw.dz=x.dz+(nm>=2);
for(int i=0;i<=1;++i)
{
if(nm-i-i<0) continue;
for(int m=i;m<=1;++m)
for(int j=0;j<=2&&i+i+j<=nm;++j)
for(int k=0;k<=2&&i+i+j+k<=nm;++k)
for(int l=0;l<=2&&i+i+j+k+l<=nm;++l)
if(~x.mz[m^i][j][k])
nw.mz[m][k][l]=min(4,max(nw.mz[m][k][l],x.mz[m^i][j][k]+j+(nm-(i+i+j+k+l))/3));
}
return nw;
}
void inii(node x)
{
if(mp.find(x)!=mp.end()) return;
mp[x]=++tt;
for(int i=0;i<=4;++i)
{
node nt=trans(x,i);
if(!hu(nt)) inii(nt);
go[mp[x]][i]=mp[nt];
}
}
int main()
{
emp.mz[0][0][0]=0;
inii(emp);
n=rd();
for(int i=1;i<=13;++i) ++cn[rd()],rd();
fac[0]=1;
for(int i=1;i<=n*4;++i) fac[i]=1ll*fac[i-1]*i%mod;
iac[n*4]=fpow(fac[n*4],mod-2);
for(int i=n*4;i;--i) iac[i-1]=1ll*iac[i]*i%mod;
int nw=1,la=0;
f[la][1][0]=1;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=tt;++j)
for(int k=0;k<=n*4;++k)
{
if(!f[la][j][k]) continue;
for(int l=cn[i];l<=4;++l)
if(go[j][l]) f[nw][go[j][l]][k+l]=(f[nw][go[j][l]][k+l]+1ll*f[la][j][k]*C(4-cn[i],l-cn[i])%mod)%mod;
f[la][j][k]=0;
}
nw^=1,la^=1;
}
int ans=0;
for(int j=1;j<=tt;++j)
for(int k=13;k<=n*4;++k)
ans=(ans+1ll*f[la][j][k]*fac[k-13]%mod*fac[n*4-k]%mod)%mod;
printf("%lld
",1ll*ans*iac[n*4-13]%mod);
return 0;
}