zoukankan      html  css  js  c++  java
  • [ZJOI2019]麻将

    这是一道麻将自动机的模板题(雾

    其实这是一道dp套dp借助自动机实现的麻将好题!

    首先把期望转化一下,拆成sigema p(x>i)

    现在要计算i张牌不胡的概率,也就等价于计算i张牌不胡的方案数。

    如果我们能建立一个关于麻将的自动机,支持插入麻将,判断当前牌型是否能胡,既可以在麻将自动机上dp解决本题。

    先考虑如何压缩一副麻将,显然只需要把它的每一种牌型的个数算出来即可。

    再考虑如何判定一副麻将是否能胡,这个可以借助dp。

    dp[i][j][k]表示当前有i个x,还额外预留了j个(x,x-1),k表示是否有对子。

    转移的时候枚举下一个位置预留几组,走个顺子即可。

    现在我们要对这个过程建立自动机。

    考虑两个麻将状态不同,当且仅当它们存在某种相同的状态,dp值却互不相同。

    写一个结构体,然后bfs寻找状态+map判重即可。

    不同的状态数不多,2000多种的样子。

    建好这个东西后再去dp就可以了。

    #include<bits/stdc++.h>
    #define N 1100
    #define M 5000
    #define db double
    #define ll long long
    #define ldb long double
    using namespace std;
    inline int read()
    {
    	char ch=0;
    	int x=0,flag=1;
    	while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*flag;
    }
    const int mo=998244353,inf=1e9+7;
    struct node
    {
    	int cnt,f[3][3][2];
    	void clear()
    	{
    		cnt=0;
    		for(int i=0;i<3;i++)for(int j=0;j<3;j++)for(int k=0;k<2;k++)f[i][j][k]=-inf;
    	}
    };
    bool operator<(node a,node b)
    {
    	for(int i=0;i<3;i++)for(int j=0;j<3;j++)for(int k=0;k<2;k++)
    	if(a.f[i][j][k]!=b.f[i][j][k])return a.f[i][j][k]<b.f[i][j][k];
    	return a.cnt<b.cnt;
    }
    bool operator==(node a,node b)
    {
    	for(int i=0;i<3;i++)for(int j=0;j<3;j++)for(int k=0;k<2;k++)
    	if(a.f[i][j][k]!=b.f[i][j][k])return false;
    	if(a.cnt!=b.cnt)return false;
    	return true;
    }
    node goal;
    void up(int &x,int k){x=max(x,k);}
    node operator+(node v,int t)
    {
    	if(v==goal)return goal;
    	static node ans;ans.clear();
    	ans.cnt=v.cnt+(t>=2);
    	if(ans.cnt>=7)return goal;
    	for(int i=0;i<3;i++)for(int j=0;j<3;j++)for(int k=0;k<2;k++)
    	{
    		int o=v.f[i][j][k];
    		if(o<0)continue;
    		for(int a=0;a<=min(i,t);a++)//枚举预留组数 
    		for(int b=0;b<=min(j,t);b++)//枚举走几个顺子
    		if(a+b<=t)
    		{
    			for(int c=0;c<=1;c++)//枚举走几个对子
    			for(int d=0;d<=1;d++)//枚举走几个面子
    			if(a+b+c*2+d*3<=t)up(ans.f[min(2,t-a-b-c*2-d*3)][a][k|c],o+b+d);
    		}
    	}
    	for(int i=0;i<3;i++)for(int j=0;j<3;j++)for(int k=0;k<2;k++)
    	{
    		ans.f[i][j][k]=min(ans.f[i][j][k],4);
    		if(k==1&&ans.f[i][j][k]==4)return goal;
    	}
    	return ans;
    }
    node qwq[M];
    queue<node>q;
    map<node,int>mp;
    int size,cnt[N],fac[N],C[N][N],dp[N][M],DP[N][M],nxt[M][10];
    void build()
    {
    	node st;
    	st.clear();goal.clear();
    	st.f[0][0][0]=0;goal.cnt=-1;
    	for(int i=0;i<3;i++)for(int j=0;j<3;j++)for(int k=0;k<2;k++)goal.f[i][j][k]=-mo;
    	mp[st]=++size;mp[goal]=++size;q.push(st);
    	for(int i=0;i<=4;i++)nxt[2][i]=2;
    	while(!q.empty())
    	{
    		node x=q.front();q.pop();
    		qwq[mp[x]]=x;
    		for(int i=0;i<=4;i++)
    		{
    			node to=x+i;
    			if(!mp.count(to))mp[to]=++size,q.push(to);
    			nxt[mp[x]][i]=mp[to];
    		}
    	}
    }
    int ksm(int x,int k)
    {
    	int ans=1;
    	while(k){if(k&1)ans=1ll*ans*x%mo;k>>=1;x=1ll*x*x%mo;}
    	return ans;
    }
    int main()
    {
    	build();
    	int n=read(),ans=0;
    	for(int i=1;i<=13;i++){cnt[read()]++;read();}
    	fac[0]=C[0][0]=1;
    	for(int i=1;i<=4*n;i++)
    	{
    		C[i][0]=1;fac[i]=1ll*fac[i-1]*i%mo;
    		for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
    	}
    	DP[0][1]=1;
    	for(int o=1;o<=n;o++)
    	{
    		for(int i=0;i<=4*o;i++)
    		for(int x=1;x<=size;x++)
    		dp[i][x]=DP[i][x],DP[i][x]=0;
    		for(int i=0;i<=4*o;i++)
    		for(int x=1;x<=size;x++)
    		{
    			if(!dp[i][x])continue;
    			for(int k=cnt[o];k<=4;k++)
    			{
    				int to=nxt[x][k];if(to==2)continue;
    				DP[i+k][to]=(DP[i+k][to]+(1ll*C[4-cnt[o]][k-cnt[o]]*dp[i][x]%mo))%mo;
    			}
    		}
    	}
    	for(int i=0;i<4*n;i++)
    	{
    		int tot=0;
    		for(int x=1;x<=size;x++)tot=(tot+DP[i][x])%mo;
    		tot=1ll*tot*fac[i-13]%mo*fac[4*n-i]%mo;
    		ans=(ans+1ll*tot*ksm(fac[4*n-13],mo-2))%mo;
    	}
    	printf("%d",(ans%mo+mo)%mo);
    	return 0;
    }
    
  • 相关阅读:
    linux 命令——48 watch (转)
    linux 命令——47 iostat (转)
    linux 命令——46 vmstat(转)
    linux 命令——45 free(转)
    linux 命令——44 top (转)
    linux 命令——43 killall(转)
    linux 命令——42 kill (转)
    linux 命令——41 ps(转)
    linux 命令——40 wc (转)
    Java for LeetCode 068 Text Justification
  • 原文地址:https://www.cnblogs.com/Creed-qwq/p/10779675.html
Copyright © 2011-2022 走看看