题解 P3706 【[SDOI2017]硬币游戏】
bztMinamoto · · 题解
题面
传送门
题解
这里介绍一下简单易懂的概率生成函数
概率生成函数
我们定义一个形式幂级数
容易发现以下几个性质
1.
2.
本题
我们先来考虑一个弱化版的问题,即
这也太弱化了吧你随便抓个
不过我们现在考虑的是求此时扔硬币的期望次数是多少(实际上这个问题就是P4548 [CTSC2006]歌唱王国,你可以一并解决了)
这里我们假设它扔的不是个硬币而是个有
我们定义一个字符串的前缀
定义
定义答案的概率生成函数为
那么容易发现两个性质
1.
也就是说
2.
即如果我们在一个未结束的串后面加上整个
对于
即我们所需要求的
对于
又因为
那么只要哈希就可以
n> 1
以上是
因为证明基本和上面差不多,下面我就不给证明直接放柿子了
定义
定义
定义
容易得到
以及
前一个柿子这里就不用管了,我们考虑后一个柿子,把
对于每一个
等会儿好像还是不能解啊……
我们再转过头来看看……
这样就有
//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
const int N=305,P=1e9+7;const double eps=1e-10;
double mp[N][N],b[N];char s[N];int bin[N],h[N][N],n,m;
inline int Hash(R int i,R int l,R int r){return ((h[i][r]-1ll*h[i][l-1]*bin[r-l+1])%P+P)%P;}
void Gauss(int n){
fp(i,1,n){
if(mp[i][i]>-eps&&mp[i][i]<eps){
fp(j,i+1,n)if(mp[j][i]<-eps||mp[j][i]>eps){
fp(k,i,n+1)swap(mp[i][k],mp[j][k]);
break;
}
}
double t=1.0/mp[i][i];fp(j,i,n+1)mp[i][j]*=t;
fp(j,i+1,n){
t=mp[j][i];
fp(k,i,n+1)mp[j][k]-=mp[i][k]*t;
}
}
fd(i,n-1,1)fp(j,i+1,n)mp[i][n+1]-=mp[j][n+1]*mp[i][j];
}
int main(){
// freopen("testdata.in","r",stdin);
scanf("%d%d",&n,&m);
bin[0]=b[0]=1;
fp(i,1,m)bin[i]=(bin[i-1]<<1)%P,b[i]=b[i-1]*2;
fp(i,1,n){
scanf("%s",s+1);
fp(j,1,m)h[i][j]=((h[i][j-1]<<1)+(s[j]=='H'))%P;
}
fp(i,1,n){
fp(j,1,n)fp(k,1,m)(Hash(i,1,k)==Hash(j,m-k+1,m))?mp[i][j]+=b[k]:0;
mp[i][n+1]=-1;
}
fp(i,1,n)mp[n+1][i]=1;mp[n+1][n+2]=1;
Gauss(n+1);
fp(i,1,n)printf("%.8lf\n",mp[i][n+2]);
return 0;
}