[SDOI2017]硬币游戏

description

题面
周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利。

大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了。

同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况。

用H表示正面朝上,用T表示反面朝上,扔很多次硬币后,会得到一个硬币序列。比如HTT表示第一次正面朝上,后两次反面朝上。

但扔到什么时候停止呢?大家提议,选出n个同学,每个同学猜一个长度为m的序列,当某一个同学猜的序列在硬币序列中出现时,就不再扔硬币了,并且这个同学胜利,为了保证只有一个同学胜利,同学们猜的n个序列两两不同。

很快,n个同学猜好序列,然后进入了紧张而又刺激的扔硬币环节。你想知道,如果硬币正反面朝上的概率相同,每个同学胜利的概率是多少。

data range

[1≤n,m≤300 ]

solution

概率生成函数

[f(x)=Pr(x=i)x^i ]

(Pr(x=i))表示(x)(i)时的概率。
有一个很好的性质:

[E(x)=sum_{i=1}^{infty}iPr(x=i)=f'(1) ]

(F_i(x))表示关于第(i)个序列的成功生成函数,(G(x))表示关于第(i)个序列的未成功生成函数,
我们要求的是(F_i(1))

考虑性质,得出

[sum_{i=1}^{n}F_i(x)+G(x)=xG(x)+1 ]

[G(x)(frac{1}{2}x)^m=sum_{j=1}^{n}sum_{k=1}^{m}a_{i,j,k}F_j(x)(frac{1}{2}x)^{m-k} ]

其中(a_{i,j,k})表示第(i)个序列的前(k)位是否为第(j)个序列的后(k)

对第一个式子求导,得出

[sum_{i=1}^{n}F_i'(x)=G(x) ]

(x=1)代入第二个式子,得出

[G(1)=sum_{j=1}^{n}sum_{k=1}^{m}a_{i,j,k}2^kF_j(1) ]

再由

[sum_{i=1}^{n}F_i(1)=1 ]

这样就有((n+1))个式子可以求解(F_i(1))(G(1))

code

顺手写了一波从未写过的哈希

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<ctime>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define FILE "a"
#define mp make_pair
#define pb push_back
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int mod=1e9+7;
const int N=310;
const dd pi=acos(-1);
const int inf=2147483647;
const ll INF=1e18+1;
il ll read(){
	RG ll data=0,w=1;RG char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
	return data*w;
}

il void file(){
	srand(time(NULL)+rand());
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
}

dd S[N][N];
il void gauss(int n){
	for(RG int i=1;i<=n;i++){
		if(abs(S[i][i])<=eps)
			for(RG int j=i+1;j<=n;j++)
				if(abs(S[j][i])>eps)
				{swap(S[i],S[j]);break;}
		for(RG int j=i+1;j<=n;j++)
			for(RG int k=n+1;k>=i;k--)
				S[j][k]-=S[i][k]*S[j][i]/S[i][i];
	}
	
	for(RG int i=n;i;i--){
		for(RG int j=n;j>i;j--){
			S[i][n+1]-=S[i][j]*S[j][n+1];
			S[i][j]=0;
		}
		S[i][n+1]/=S[i][i];
		S[i][i]=1;
	}
}

ll n,m,pw[N],s[N][N],h[N][N];
il int hash(int i,int l,int r){
	return (h[i][r]-1ll*h[i][l-1]*pw[r-l+1]%mod+mod)%mod;
}
bool a[N][N][N];
dd pww[N];
int main()
{
	n=read();m=read();RG char c;pw[0]=pww[0]=1;
	for(RG int i=1;i<=m;i++)
		pw[i]=2ll*pw[i-1]%mod,pww[i]=pww[i-1]*2;
	for(RG int i=1;i<=n;i++)
		for(RG int j=1;j<=m;j++){
			c=0;while(c!='T'&&c!='H')c=getchar();
			s[i][j]=(c=='H');
			h[i][j]=(2ll*h[i][j-1]%mod+s[i][j])%mod;
		}
	for(RG int i=1;i<=n;i++)
		for(RG int j=1;j<=n;j++)
			for(RG int k=1;k<=m;k++)
				a[i][j][k]=(hash(i,1,k)==hash(j,m-k+1,m));
	for(RG int i=1;i<=n;i++){
		for(RG int j=1;j<=n;j++)
			for(RG int k=1;k<=m;k++)
				S[i][j]+=a[i][j][k]*pww[k];
		S[i][n+1]=-1;
	}
	for(RG int i=1;i<=n;i++)S[n+1][i]=1;S[n+1][n+2]=1;
	gauss(n+1);
	for(RG int i=1;i<=n;i++)printf("%.10lf
",S[i][n+2]);
	return 0;
}

原文地址:https://www.cnblogs.com/cjfdf/p/9318497.html