洛谷ios
先作一点小铺垫,对于一堆牌而言,咱们只须要知道这\(n\)张牌分别出现的次数就好了,即咱们只须要知道一个长度为\(n\)的串就能够了。
首先考虑如何判断一副牌是否是能胡。
出现了七对牌的状况很容易特判处理掉,只须要考虑第一种状况。
那么咱们考虑\(dp\)来判断,设\(f[i][j][k][0/1]\)表示的当前考虑到了这个字符串的第\(i\)位,即考虑到了第\(i\)种牌,\(i-1,i,i+1\)的对子要用\(j\)次,\(i,i+1,i+2\)的对子要用\(k\)次,是否已经出现了一个对子。而这个\(dp\)值表示的是可以留下的最大的面子数量。不难发现\(j,k\)都不会超过\(2\)。
那么转移的时候至关于读进来当前的\(i\)有多少个,假设是\(x\),接下来\(x\)减去\(j+k\)组成顺子,而后枚举一下以多少\(x\)为开头组成顺子。这里再枚举一下是否用当前的\(x\)组成对子或者刻字。
咱们把第一维丢掉,只考虑剩下的\(18\)个元素和最大的可能对子数,而且强制\(dp\)值不超过\(4\),最大对子数不超过\(7\)。这样子就会存在大量重复的状态,打表可得状态只有不到\(2100\)种。
那么咱们能够提早把全部状态所有预处理出来,预处理对于当前的一个状态,插入后面一种牌\(x\)张的结果,这样子就构成了一个自动机,那么咱们只须要从头至尾把一种状态插入进去就能够知道有没有胡牌。
那么此时咱们只须要知道抽了\(i\)张以后还未胡牌的几率,所有累加就是答案。
考虑在自动机上\(dp\),设\(f[i][p][k]\)表示当前考虑到第\(i\)种牌,且当前在自动机的\(p\)位置上,前面一共抽了\(k\)张牌且尚未胡的方案数。
转移的时候枚举这张牌用了多少次,用组合数带进去进行计算,经过自动机进行状态的转移。ui
#include<iostream> #include<cstdio> #include<cstring> #include<map> using namespace std; #define MOD 998244353 #define MAX 402 void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;} inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } struct Data { int f[18],cnt; void init(){memset(f,-1,sizeof(f));f[0]=cnt=0;} bool check() { if(cnt>=7)return true; for(int i=0;i<3;++i) for(int j=0;j<3;++j) if(f[9+i*3+j]>=4)return true; return false; } }QwQ,ST[2100]; bool operator<(Data a,Data b) { if(a.cnt!=b.cnt)return a.cnt<b.cnt; for(int i=0;i<18;++i)if(a.f[i]!=b.f[i])return a.f[i]<b.f[i]; return false; } Data Trans(Data a,int b) { Data c;c.init();c.cnt=min(a.cnt+(b>=2),7); for(int i=0;i<3;++i) for(int j=0;j<3;++j) { if(~a.f[i*3+j]) { for(int k=0;k<3&&i+j+k<=b;++k) c.f[j*3+k]=max(c.f[j*3+k],min(a.f[i*3+j]+i+(b-i-j-k>=3),4)); if(b>=2) for(int k=0;k<3&&i+j+k<=b-2;++k) c.f[9+j*3+k]=max(c.f[9+j*3+k],min(a.f[i*3+j]+i,4)); } if(~a.f[9+i*3+j]) { for(int k=0;k<3&&i+j+k<=b;++k) c.f[9+j*3+k]=max(c.f[9+j*3+k],min(a.f[9+i*3+j]+i+(b-i-j-k>=3),4)); } } return c; } map<Data,int> M;int tot; void Build(Data x) { if(x.check())return; if(M.find(x)!=M.end())return; ST[M[x]=++tot]=x; for(int i=0;i<=4;++i)Build(Trans(x,i)); } int jc[MAX],jv[MAX],inv[MAX]; int f[2][2100][MAX]; int C(int n,int m){return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;} int n,ans,s[MAX],tr[2100][5]; int main() { QwQ.init();Build(QwQ); jc[0]=jv[0]=inv[0]=inv[1]=1; for(int i=2;i<MAX;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD; for(int i=1;i<MAX;++i)jc[i]=1ll*jc[i-1]*i%MOD; for(int i=1;i<MAX;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD; n=read();for(int i=1;i<=13;++i)s[read()]+=1,read(); for(int i=1;i<=tot;++i)for(int j=0;j<=4;++j)tr[i][j]=M[Trans(ST[i],j)]; f[0][1][0]=1; for(int i=1,ss=0,nw=1,pw=0;i<=n;ss+=s[i],++i,nw^=1,pw^=1) { memset(f[nw],0,sizeof(f[nw])); for(int j=1;j<=tot;++j) for(int k=s[i];k<=4;++k) { if(!tr[j][k])continue; int w=1ll*C(4-s[i],k-s[i])*jc[k-s[i]]%MOD; for(int l=0;l<=n*4-k;++l) if(f[pw][j][l]) add(f[nw][tr[j][k]][k+l],1ll*f[pw][j][l]*w%MOD*C(k+l-ss-s[i],k-s[i])%MOD); } } for(int i=13,val=1;i<=n*4;val=1ll*val*inv[n*4-i]%MOD,++i) { int ret=0; for(int j=1;j<=tot;++j)add(ret,f[n&1][j][i]); add(ans,1ll*ret*val%MOD); } printf("%d\n",ans); return 0; }