您的位置:首页 > 其它

[BZOJ4820] 硬币游戏 - 高斯消元

2017-04-13 23:45 357 查看
题面

我好菜啊什么题都不会做.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

,B
,pw2
;
int n,m,deg;
char s

;
LL h

,h0

,pwb
,pw0
;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: