Bzoj4820 [Sdoi2017]硬币游戏

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 212  Solved: 87

Description

周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利。大家纷纷觉得这个游戏非常符

合同学们的特色,但只是扔硬币实在是太单调了。同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币

,其他同学记录下正反面情况。用H表示正面朝上,用T表示反面朝上,扔很多次硬币后,会得到一个硬币序列。比

如HTT表示第一次正面朝上,后两次反面朝上。但扔到什么时候停止呢?大家提议,选出n个同学,每个同学猜一个

长度为m的序列,当某一个同学猜的序列在硬币序列中出现时,就不再扔硬币了,并且这个同学胜利,为了保证只

有一个同学胜利,同学们猜的n个序列两两不同。很快,n个同学猜好序列,然后进入了紧张而又刺激的扔硬币环节

。你想知道,如果硬币正反面朝上的概率相同,每个同学胜利的概率是多少。

Input

第一行两个整数n,m。

接下里n行,每行一个长度为m的字符串,表示第i个同学猜的序列。

1<=n,m<=300

Output

输出n行,第i行表示第i个同学胜利的概率。

输出与标准输出的绝对误差不超过10^-6即视为正确。

Sample Input

3 3
THT
TTH
HTT

Sample Output

0.3333333333
0.2500000000
0.4166666667

HINT

Source

鸣谢infinityedge上传

数学问题 概率 高斯消元

kmp能想到,高斯消元能想到,然而不会列这个方程,沮丧

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 using namespace std;
 7 const int mxn=305;
 8 int read(){
 9     int x=0,f=1;char ch=getchar();
10     while(ch<‘0‘ || ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
11     while(ch>=‘0‘ && ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
12     return x*f;
13 }
14 double f[mxn][mxn];
15 double p[mxn];
16 void Gauss(int n){
17     int i,j;
18     for(i=1;i<=n;i++){
19         int p=i;
20         for(j=i+1;j<=n;j++)  if(fabs(f[j][i])>fabs(f[p][i]))p=j;
21         if(p!=i)for(j=i;j<=n+1;j++)swap(f[i][j],f[p][j]);
22         for(j=n+1;j>=i;j--)  f[i][j]/=f[i][i];
23         for(j=1;j<=n;j++){
24             if(j==i)continue;
25             double tmp=f[j][i];
26             for(int k=1;k<=n+1;k++)
27                 f[j][k]-=f[i][k]*tmp;
28         }
29     }
30     return;
31 }
32 int nxt[mxn];
33 char s[mxn][mxn];
34 int n,m;
35 void getfail(int id){
36     nxt[1]=0;
37     for(int i=2,j=0;i<=m;i++){
38         while(j && s[id][i]!=s[id][j+1])j=nxt[j];
39         if(s[id][j+1]==s[id][i])j++;
40         nxt[i]=j;
41     }
42     return;
43 }
44 void KMP(int id){
45     for(int i=1;i<=n;i++){//和所有串匹配
46         int j=0;
47         for(int k=1;k<=m;k++){
48             while(j && s[id][j+1]!=s[i][k])j=nxt[j];
49             if(s[id][j+1]==s[i][k])j++;
50         }
51         for(;j;j=nxt[j])f[id][i]+=p[m-j];//,printf("%d to %d :%.5f\n",id,i,p[m-j]);
52     }
53     f[id][n+1]=-p[m];
54     f[n+1][id]=1.0;
55     return;
56 }
57 void Print(){
58     for(int i=1;i<=n+1;i++){
59         for(int j=1;j<=n+2;j++){
60             printf("%.4lf ",f[i][j]);
61         }
62         puts("");
63     }
64     return;
65 }
66 int main(){
67     int i,j;
68     n=read();m=read();
69     p[0]=1.0;
70     for(i=1;i<=m;i++)p[i]=p[i-1]*0.5;
71     for(i=1;i<=n;i++)scanf("%s",s[i]+1);
72     for(i=1;i<=n;i++){
73         getfail(i);
74         KMP(i);
75     }
76     f[n+1][n+2]=1.0;
77 //  Print();
78     Gauss(n+1);
79     for(int i=1;i<=n;i++)
80         printf("%.10f\n",f[i][n+2]);
81     return 0;
82 }
83 
时间: 2024-10-12 21:31:12

Bzoj4820 [Sdoi2017]硬币游戏的相关文章

【BZOJ4820】[Sdoi2017]硬币游戏 AC自动机+概率DP+高斯消元

[BZOJ4820][Sdoi2017]硬币游戏 Description 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利.大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了.同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况.用H表示正面朝上,用T表示反面朝上,扔很多次硬币后,会得到一个硬币序列.比如HTT表示第一次正面朝上,后两次反面朝上.但扔到什么时候停止呢?大家提议,选出n个同学,每个同学猜一个长度为m的序列,当某

【求助】「luogu3706」[SDOI2017]硬币游戏

给题解跪辣! 这做法太神了.. 看到Accepted之前我一直不信这程序能AC. 因为方程系数的数量级是从10-1到10-91的! 用double储存跑高斯消元精度还不会爆炸!! 谁能告诉我是为什么 1 #include<bits/stdc++.h> 2 #define db double 3 using namespace std; 4 const int N=310,M=90010; 5 const db eps=1e-10; 6 int n,m; 7 int ch[M][2],fail[

SDOI2017硬币游戏

题面链接 洛咕 sol 神题,幸好我不是SD的QAQ. 假设你们都会\(O(n^3m^3)\)的高斯消元,具体来说就是建出\(Trie\)图然后套游走的板子. 然后我们发现可以把不能匹配任何串的概率压到一起. 考虑一个不能匹配任何串的\(S\).一个串\(A_i\)获胜当且仅当最后串是这样的:\(S+A_i\). 真的吗? 如果\(S\)的后缀和\(A_i\)的前缀能拼出来\(A_j\)就假掉了.所以神仙们采用了神仙做法. 引用\(Kelin\)神犇的例子. 举个例子设\(A=101,B=110

[SDOI2017]硬币游戏

考虑令\(p_i\)表示第\(i\)个人赢的概率,那么显然我们可以得到. \[ p_i+\sum_{j=1}^np_j(\sum_{k=1}^{m-[i==j]}[prefix(i,k)=suffix(j,k)]\frac{1}{2^{m-k}}) = \frac{1}{2^m} \] 然后高斯消元即可. /* mail: [email protected] author: MLEAutoMaton This Code is made by MLEAutoMaton */ #include<st

loj2004. 「SDOI2017」硬币游戏

2004. 「SDOI2017」硬币游戏 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利. 大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了. 同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况. 用 $ \texttt{H} $ 表示正面朝上, 用 $ \texttt{T} $ 表示反面朝上,扔很多次硬币后,会得到一个硬币序列.比如 $ \texttt{HTT} $ 表示第一次正面朝上,后两次反面朝上. 但扔到什么时

1381 硬币游戏

1381 硬币游戏 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 有一个简单但是很有趣的游戏.在这个游戏中有一个硬币还有一张桌子,这张桌子上有很多平行线(如下图所示).两条相邻平行线之间的距离是1,硬币的半径是R,然后我们来抛硬币到桌子上,抛下之后硬币有时候会和一些直线相交(相切的情况也算是相交),有时候不会. 请你来计算一下抛一次硬币之后,该硬币和直线相交数目的期望. Input 第一行给出一个整数T,表示有T组数据(1<=T<=10000). 第2行到T

bzoj2017[Usaco2009 Nov]硬币游戏*

bzoj2017[Usaco2009 Nov]硬币游戏 题意: 初始时,一个有N枚硬币的堆栈放在地上,每枚硬币都有一个价值.开始玩游戏时,第一个玩家可以从堆顶拿走一枚或两枚硬币.之后每一轮中,当前的玩家至少拿走一枚硬币,至多拿走对手上一次所拿硬币数量的两倍.当没有硬币可拿时,游戏结束. 两个玩家都希望拿到最多钱数的硬币.求第一个玩家最多能拿多少钱.n≤2000. 题解: 首先有dp方程:f[i][j][0]=max(f[i][k][1]+sum(i-k,i)),1≤k≤min(j*2,i),f[

HDU 3537 Mock Turtles型翻硬币游戏

题目大意: 每次可以翻1个或者2个或者3个硬币,但要保证最右边的那个硬币是正面的,直到不能操作为输,这题目还有说因为主人公感情混乱可能描述不清会有重复的硬币说出,所以要去重 这是一个Mock Turtles型翻硬币游戏 下面是对这个类型游戏的讲解 约束条件6:每次可以翻动一个.二个或三个硬币.(Mock Turtles游戏) 初始编号从0开始. 当N==1时,硬币为:正,先手必胜,所以sg[0]=1. 当N==2时,硬币为:反正,先手必赢,先手操作后可能为:反反或正反,方案数为2,所以sg[1]

51Nod - 1381 硬币游戏

51Nod - 1381 硬币游戏 有一个简单但是很有趣的游戏.在这个游戏中有一个硬币还有一张桌子,这张桌子上有很多平行线(如下图所示).两条相邻平行线之间的距离是1,硬币的半径是R,然后我们来抛硬币到桌子上,抛下之后硬币有时候会和一些直线相交(相切的情况也算是相交),有时候不会. 请你来计算一下抛一次硬币之后,该硬币和直线相交数目的期望. Input 第一行给出一个整数T,表示有T组数据(1<=T<=10000). 第2行到T+1,每行给出一个整数R.(0< R <= 10,00