概率生成函数学习笔记
[CTSC2006]歌唱王国
这个更详细的全部解题过程详见 M_sea 的题解,他写的很好。
我就讲一下为什么 \(G(x)(\dfrac xn)^m=\sum\limits_{i=1}^m a_i (\dfrac xn)^{m-i} F(x)\) 成立。
左式意义是对于当前的一个还未结束的串,我们在其后面加上一个整串的概率。
易知此时已经结束,则我们枚举结束时间,要求这个时间结束后再填完整串,故这个后缀必须是一个 border
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int base=19260817;
const int mod=1e9+7;
const int MOD=1e4;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,T,m,a[maxn],hs[maxn],pw[maxn],res;
inline int calc(int l,int r){return (hs[r]-1ll*hs[l-1]*pw[r-l+1]%mod+mod)%mod;}
inline void solve(){
m=read();res=0;
for(int i=1;i<=m;i++){
a[i]=read();
hs[i]=(a[i]+1ll*hs[i-1]*base)%mod;
}
for(int i=1,w=1;i<=m;i++){
w=w*n%MOD;
if(calc(1,i)==calc(m-i+1,m))res=(res+w)%MOD;
}int tmp=1000;
for(int i=1;i<=4;i++){
printf("%d",res/tmp);
res%=tmp;tmp/=10;
}puts("");
}
int main(){
n=read(),T=read();
pw[0]=1;
for(int i=1;i<=100000;i++)
pw[i]=1ll*pw[i-1]*base%mod;
while(T--)solve();
return 0;
}
[SDOI2017]硬币游戏
相似的做法,核心还是那个式子,只是变成了多元,于是考虑高斯消元即可。
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int base=131;
const int mod=1e9+7;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
const int N=305;
int n,m,hs[N][N],pw[N];char c[N][N];
double a[N][N],p2[N];
inline int calc(int id,int l,int r){return (hs[id][r]-1ll*hs[id][l-1]*pw[r-l+1]%mod+mod)%mod;}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
scanf("%s",c[i]+1);
for(int j=1;j<=m;j++)
hs[i][j]=(1ll*hs[i][j-1]*base+c[i][j]-'A'+1)%mod;
}pw[0]=p2[0]=1;a[n+1][n+2]=1;
for(int i=1;i<=m;i++)
pw[i]=1ll*pw[i-1]*base%mod,p2[i]=p2[i-1]/2.0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=m;k++)
if(calc(i,1,k)==calc(j,m-k+1,m))a[i][j]+=p2[m-k];
for(int i=1;i<=n;i++)a[i][n+1]=-p2[m],a[n+1][i]=1;
for(int i=1;i<=n+1;i++){
int pos=i;
for(int j=i+1;j<=n+1;j++)
if(fabs(a[pos][i])<fabs(a[j][i]))pos=j;
if(pos^i)for(int j=1;j<=n+2;j++)
swap(a[i][j],a[pos][j]);
for(int j=1;j<=n+1;j++){
if(i==j)continue;
double k=a[j][i]/a[i][i];
for(int p=i;p<=n+2;p++)
a[j][p]-=a[i][p]*k;
}
}
for(int i=1;i<=n;i++)
printf("%.8lf\n",a[i][n+2]/a[i][i]);
return 0;
}