1. 程式人生 > 實用技巧 >模擬賽38 D. 遊戲 概率與期望

模擬賽38 D. 遊戲 概率與期望

題目描述

小苗在和神奈子與諏訪子玩遊戲。 一開始三個人每個人都有若干籌碼,記為 \(x,y,z\)

每一輪籌碼最多的玩家會將自己的一個籌碼隨機給另外兩個玩家之一。

如果有多個籌碼最多的玩家,則隨機選擇一個給籌碼。當所有人的籌碼數量一樣時,遊戲結束。 小苗想知道遊戲結束的期望輪數 \(mod\ 998244353\) 的值;如果期望值為無窮大,則輸出 \(-1\)

輸入格式

第一行一個正整數 \(T\),表示資料組數。接下來 \(T\) 組資料,對於每組資料:

共一行包含三個正整數 \(x,y,z\)

輸出格式

對於每組資料:

若期望為無窮大,輸出 \(-1\);否則輸出期望值 \(mod\ 998244353\)

的結果。

樣例

樣例輸入1

3
1 2 3
1 1 4
5 1 4

樣例輸出1

2
3
-1

樣例輸入2

3
114 514 191
2333 3333 1
99824 435 4

樣例輸出2

244519070
3776
23441972

資料範圍與提示

分析

如果 \((x+y+z)\%3!=0\) ,輸出 \(-1\)

這道題移動的方式只與籌碼的差值有關,與具體有多少籌碼無關

\(f[i][j]\) 為籌碼最多的人與另兩個人的籌碼分別相差 \(i\)\(j\) 時遊戲結束的期望輪數、

初始時 \(f[0][0]=0\)

轉移 \(f[i][j]=\frac{1}{2}(f[i-2][j-1]+f[i-1][j-2])+1\)

特殊地,有 \(f[0][i]=\frac{1}{2}(f[2][i+1]+f[1][i-1])+1\)

\(f[1][i]=\frac{1}{2}(f[1][i]+f[0][i-2])+1\)

把最後一個式子化簡,可以得到

\(f[1][i]=f[0][i-2]+2\)

把第二個式子的 \(f[2][i+1]\) 用第一個式子替換,就可以得到

\(f[0][i]=f[1][i-1]+2\)

這樣,我們就可以手動算出 \(f[0][i]\)\(f[1][i]\) 的值

就有一個 \(n^2\) 的暴力解法

繼續推導會發現,轉移的狀態是一個樹的結構

那麼我們就可以列舉最後的狀態,看一看轉移到這個狀態有多少種方法

因為 \(a+b+c\) 不會很大,所以狀態數不會很多

那麼我們就可以算出轉移到這個狀態分別進行了多少次 \((-1,-2)\)\((-2,-1)\) 的操作

用一個組合數就可以了

程式碼

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#define rg register
const int maxn=1e6+5;
const int mod=998244353;
int t,a[maxn],b[maxn],c[maxn],f[3][maxn],ny[maxn],jc[maxn],jcc[maxn],mi[maxn],con[maxn];
void pre(){
	ny[1]=1;
	for(rg int i=2;i<maxn;i++){
		ny[i]=1LL*(mod-mod/i)*ny[mod%i]%mod;
	}
	jc[0]=jcc[0]=mi[0]=1;
	for(rg int i=1;i<maxn;i++){
		jc[i]=1LL*jc[i-1]*i%mod;
		jcc[i]=1LL*jcc[i-1]*ny[i]%mod;
		mi[i]=1LL*mi[i-1]*ny[2]%mod;
		con[i]=1LL*mi[i]*i%mod;
	}
	for(rg int i=2;i<maxn;i++){
		if(i%3==0) f[0][i]=f[1][i-1]+2;
		if((i+1)%3==0) f[1][i]=f[0][i-2]+2;
	}
}
int js(int aa,int bb,int cc,int dd,int dep){
	if(cc<0 || dd<0 || (cc==0 && dd==0)) return 0;
	rg int cz1=aa-cc,cz2=bb-dd,cnt1=0,cnt2=0;
	if(cc==0) cz1-=2,cz2--;
	cnt1=cnt2=(cz1+cz2-std::abs(cz1-cz2)*3)/6;
	if(cnt1<0 || cnt2<0) return 0;
	cz1-=cnt1*2;
	cz2-=cnt2*2;
	if(cz1>cz2) cnt2+=cz1-cz2;
	else cnt1+=cz2-cz1;
	rg int nans=0;
	nans=1LL*jc[cnt1+cnt2]*jcc[cnt1]%mod*jcc[cnt2]%mod;
	nans=1LL*f[cc][dd]%mod*mi[dep]%mod*nans%mod+1LL*con[dep]*nans%mod;
	if(nans>=mod) nans-=mod;
	return nans;
}
void solve(int aa,int bb){
	rg int nowans=0;
	rg int mindep=aa/2,maxdep=(aa+bb)/3,nowsum;
	for(rg int i=mindep;i<=maxdep;i++){
		nowsum=aa+bb-i*3;
		nowans+=js(aa,bb,0,nowsum,i);
		if(nowans>=mod) nowans-=mod;
		nowans+=js(bb,aa,0,nowsum,i);
		if(nowans>=mod) nowans-=mod;
		nowans+=js(aa,bb,1,nowsum-1,i);
		if(nowans>=mod) nowans-=mod;
		nowans+=js(bb,aa,1,nowsum-1,i);
		if(nowans>=mod) nowans-=mod;
	}
	printf("%d\n",nowans);
}
int main(){
	pre();
	scanf("%d",&t);
	for(rg int i=1;i<=t;i++){
		scanf("%d%d%d",&a[i],&b[i],&c[i]);
		if((a[i]+b[i]+c[i])%3!=0){
			printf("-1\n");
			continue;
		}
		if(a[i]>b[i]) std::swap(a[i],b[i]);
		if(b[i]>c[i]) std::swap(b[i],c[i]);
		if(a[i]>b[i]) std::swap(a[i],b[i]);
		solve(c[i]-b[i],c[i]-a[i]);
	}
	return 0;
}