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