• [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;
    }
    
    
  • 相关阅读:
    要求两个条件都为假时,执行某些操作
    Celery + RabbitMq 示意图
    关于消息队列的好文章
    django related_name, on_delete
    Celery 图,[转]
    django model 中 meta子类详解
    django 自定义app
    python __dict__
    Python SQLAlchemy基本操作和常用技巧(包含大量实例,非常好)【转】
    scala private
  • 原文地址:https://www.cnblogs.com/cjfdf/p/9318497.html
Copyright © 2020-2023  润新知