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;
    }
    
  • 相关阅读:
    MyBatis与spring面试题-转载
    122. 买卖股票的最佳时机 II(贪心策略)
    121. 买卖股票的最佳时机
    120. 三角形最小路径和
    236. 二叉树的最近公共祖先(快手面试)
    b,b+树区别
    119. 杨辉三角 II
    118. 杨辉三角
    检查型异常(Checked Exception)与非检查型异常(Unchecked Exception)
    Redis
  • 原文地址:https://www.cnblogs.com/Creed-qwq/p/10779675.html
Copyright © 2011-2022 走看看