1. 程式人生 > >[BZOJ4820] 硬幣遊戲

[BZOJ4820] 硬幣遊戲

題面

我好菜啊什麼題都不會做.jpg

看了網上的題解還是沒懂問了問wlp才似乎懂了一點點 本文僅代表作者觀點 如有錯誤 懇請指正。

看了題感覺就像前些天浙江講課的某題,由於作者太蒻,完全記不得當時講了寫什麼了。。

題意就是給一堆字符集為2的長度為m的字串 隨機生成字元 在生成的字串的末尾出現給定的一堆的串中的一個串時這個串勝利,求每個串的勝利概率。

那麼我們假設在沒有失配時的狀態為N,我們強制在它後面新增m個字元,對每個串我們新增一次,顯然這樣出現的概率是相同的,設為P1

那麼P1=什麼呢?設當前這個串的勝利概率為A,則P1=A+P(在出現串A之前其他串勝利了) 顯然這些串必然存在後綴是串A的字首

我們可以考慮列舉每個串 來計算其在這裡貢獻的係數 如果我們知道了關於每個串的勝利概率的係數 我們就能得到N+1個未知數和N個方程 最後一個方程用概率和為1來湊就可以了

現在問題就是如何計算這些係數。

我們考慮 因為對於每個串勝利的情況 其他串獲勝的情況都被排除了 所以串與串之間獨立 可以單獨考慮 因此我們假設N的後x個字元和A的前m-x個字元構成了串B 因為狀態數無限 出現這樣情況的概率是0.5^x 因此設B串獲勝的概率為P' 在這種情況下B獲勝的概率就是0.5^x*P' 因為我強制加了m個字元 因此這樣的所有情況的概率和就是相同的(遊戲一定會終止),都是P1,因此就可以做了。

upd. 感謝yql發現我的一個錯誤 已更正

程式碼貼一下:

#include"bits/stdc++.h"

using namespace std;
typedef long long LL;

const int N=305,Base=107,P=998244353;

double A[N][N],B[N],pw2[N];
int n,m,deg;
char s[N][N];
LL h[N][N],h0[N][N],pwb[N],pw0[N];

int main(){
#ifndef ONLINE_JUDGE
	freopen("bzoj4820.in","r",stdin);
	freopen("bzoj4820.out","w",stdout);
#endif
	scanf("%d%d",&n,&m);
	deg=n+1;
	for(int i=1;i<=n;i++){
		scanf("%s",s[i]+1);
		for(int j=1;j<=m;j++){
			h[i][j]=h[i][j-1]*Base+s[i][j];
			h0[i][j]=(h0[i][j-1]*Base+s[i][j])%P;
		}
	}
	pw2[0]=pwb[0]=pw0[0]=1;
	for(int i=1;i<=m;i++){
		pw2[i]=pw2[i-1]*.5,pwb[i]=pwb[i-1]*Base;
		pw0[i]=pw0[i-1]*Base%P;
	}
	for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)for(int k=1;k<m;++k)
		if(h[i][k]==h[j][m]-h[j][m-k]*pwb[k]){
			LL x0=h0[i][k],x1=(h0[j][m]-h0[j][m-k]*pw0[k]%P+P)%P;
			if(x0==x1)A[i][j]+=pw2[m-k];
		}
	for(int i=1;i<=n;i++)A[i][i]++,A[i][n+1]--,A[n+1][i]++;
	B[n+1]=1;
	for(int i=1,l;i<=deg;++i){
		for(int j=(l=i)+1;j<=deg;++j)if(fabs(A[j][i])>fabs(A[l][i]))l=j;
		for(int j=i;j<=deg;++j)swap(A[i][j],A[l][j]);
		swap(B[i],B[l]);
		double t=A[i][i];
		for(int j=i;j<=deg;++j)A[i][j]/=t;
		B[i]/=t;
		for(int j=1;j<=deg;++j)if(j!=i){
			t=A[j][i];
			for(int k=i;k<=deg;++k)A[j][k]-=A[i][k]*t;
			B[j]-=B[i]*t;
		}
	}
	for(int i=1;i<=n;i++)printf("%.10lf\n",B[i]);
	return 0;
}