[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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值