zoukankan      html  css  js  c++  java
  • BZOJ 4820 [SDOI2017] 硬币游戏

    Description

      周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利。大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了。同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况。用H表示正面朝上,用T表示反面朝上,扔很多次硬币后,会得到一个硬币序列。比如HTT表示第一次正面朝上,后两次反面朝上。但扔到什么时候停止呢?大家提议,选出(n)个同学,每个同学猜一个长度为(m)的序列,当某一个同学猜的序列在硬币序列中出现时,就不再扔硬币了,并且这个同学胜利,为了保证只有一个同学胜利,同学们猜的(n)个序列两两不同。很快,(n)个同学猜好序列,然后进入了紧张而又刺激的扔硬币环节。你想知道,如果硬币正反面朝上的概率相同,每个同学胜利的概率是多少。

    Input

      第一行两个整数(n,m)
      接下里n行,每行一个长度为m的字符串,表示第i个同学猜的序列。
      (1<=n,m<=300)

    Output

      输出n行,第i行表示第i个同学胜利的概率。
      输出与标准输出的绝对误差不超过(1e-6)即视为正确。

    Sample Input

    3 3
    THT
    TTH
    HTT

    Sample Output

    0.3333333333
    0.2500000000
    0.4166666667

    数据规模

    (1leq n,m leq 300)

    乍一看是 [JSOI2009]有趣的游戏,但是数据范围不支持。于是标解就用了个十分神仙的方法减少了方程数。

    我们还是从 [JSOI2009]有趣的游戏 的思路开始分析。我们发现中间状态太多了,所以我们将转移到中间状态的期望设为(x_0)。然后(x_i (1leq i leq n))表示第(i)个人胜利的期望。

    因为该题依然期望(=)概率,所以依然有(x_1+x_2+...+x_n=1)

    然后就是最神仙的方程了。

    我们设(P(i))表示在游戏中途(未结束时)出现的所有字符串后面接上第(i)个字符串得到的字符串出现的期望。

    首先,(P(i)=frac{1}{2^m}x_0 .)我们在任意一个中间状态后面加上第(i)个字符串,就可以得到想要的结果。因为每种字符出现概率相同,所以出现第(i)个串的概率为(frac{1}{2^m})。我们再考虑用用其他的变量表示(P(i))

    显然出现了这种字符串代表游戏一定结束了,但是游戏不一定在这个时候结束,赢家不一定是(i),因为可能在插入第(i)个串的中途就匹配上了一个字符串。我们发现出现这种情况一定是一个字符串(j)的长度为(k(1leq k < m))的后缀与(i)字符串的长度为(k)的前缀相同(注意这里(j)是可以等于(i)的)。画个图就很好理解了。

    然后再在后面补上(m-k)个字符就可以了。这部分的概率是(frac{1}{2^{m-k}})

    我们考虑出现上述情况的时候赢家一定是(j),所以第(i)个字符串对(P(i))的贡献就是(g(i,j)=displaystyle sum_{k=1}^{m-1}[j的k后缀=i的k前缀]frac{1}{2^{m-k}})

    快速求出所有(k)可以考虑用(kmp)

    于是我们有列出了(n)个方程:$displaystyle sum_{j=1}^ng(i,j)x_j+x_i=frac{1}{2^m}x_0 $。

    然后解方程就行了。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<queue>
    #include<vector>
    #include<map>
    #include<iomanip>
    #define ll long long
    #define ld long double
    #define N 305
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,m;
    char s[N][N];
    ld a[N][N],pw[N];
    int nxt[N];
    void Get_nxt(char *s) {
    	memset(nxt,0,sizeof(nxt));
    	nxt[0]=-1;
    	for(int i=1;i<=m;i++) {
    		int j=nxt[i-1];
    		while(j!=-1&&s[j+1]!=s[i]) j=nxt[j];
    		nxt[i]=j+1; 
    	}
    }
    ld cal(char *s,char *t) {
    	int now=0;
    	ld ans=0;
    	for(int i=1;i<=m;i++) {
    		while(now!=-1&&s[now+1]!=t[i]) now=nxt[now];
    		now++;
    	}
    	if(now==m) now=nxt[now];
    	while(now) {
    		ans+=pw[m-now];
    		now=nxt[now];
    	}
    	return ans;
    }
    ld ans[N];
    void Guass() {
    	for(int i=0;i<=n;i++) {
    		for(int j=i;j<=n;j++)
    			if(fabs(a[i][i])<fabs(a[j][i])) swap(a[i],a[j]);
    		for(int j=i+1;j<=n;j++) {
    			ld tem=a[j][i]/a[i][i];
    			for(int k=i;k<=n+1;k++) a[j][k]-=tem*a[i][k];
    		}
    	}
    	for(int i=n;i>=1;i--) {
    		for(int j=n;j>i;j--) {
    			a[i][n+1]-=ans[j]*a[i][j];
    		}
    		ans[i]=a[i][n+1]/a[i][i];
    	}
    }
    
    int main() {
    	n=Get(),m=Get();
    	pw[0]=1;
    	for(int i=1;i<=m;i++) pw[i]=pw[i-1]*0.5;
    	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    	for(int i=1;i<=n;i++) a[0][i]=1;
    	a[0][n+1]=1;
    	for(int i=1;i<=n;i++) {
    		Get_nxt(s[i]);
    		for(int j=1;j<=n;j++) {
    			a[i][j]=cal(s[i],s[j]);
    		}
    		a[i][0]=-pw[m];
    		a[i][i]++;
    	}
    	Guass();
    	for(int i=1;i<=n;i++) cout<<fixed<<setprecision(10)<<ans[i]<<"
    ";
    	return 0;
    }
    
    
  • 相关阅读:
    java安全——BASE64
    Spring Aop 梳理
    Spring Aop中,获取被代理类的工具
    关于eclipse运行TestNG出现: CreateProcess error=206, ÎļþÃû»ò)չÃû的解决办法
    java内存设置
    用python开发调试器——起始篇
    5分钟破解全峰快递
    重拾黑客
    ThreadPoolExecutor的运转机制
    20190627_解决ADB的device offline问题的两种方法
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10356868.html
Copyright © 2011-2022 走看看