[BZOJ4820] 硬幣遊戲
阿新 • • 發佈:2019-02-13
我好菜啊什麼題都不會做.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; }