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

数据规模

\(1\leq n,m \leq 300\)

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

我们还是从 [JSOI2009]有趣的游戏 的思路开始分析。我们发现中间状态太多了,所以我们将转移到中间状态的期望设为\(x_0\)。然后\(x_i (1\leq 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(1\leq 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}{2m}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]<<"\n";
return 0;
}

BZOJ 4820 [SDOI2017] 硬币游戏的更多相关文章

  1. [BZOJ 4820] [SDOI2017] 硬币游戏(高斯消元+概率论+字符串hash)

    [BZOJ 4820] [SDOI2017] 硬币游戏(高斯消元+概率论+字符串hash) 题面 扔很多次硬币后,用H表示正面朝上,用T表示反面朝上,会得到一个硬币序列.比如HTT表示第一次正面朝上, ...

  2. BZOJ:4820: [Sdoi2017]硬币游戏&&BZOJ:1444: [Jsoi2009]有趣的游戏(高斯消元求概率)

    1444: [Jsoi2009]有趣的游戏 4820: [Sdoi2017]硬币游戏 这两道题都是关于不断随机生成字符后求出现给定字符串的概率的问题. 第一题数据范围较小,将串建成AC自动机以后,以A ...

  3. BZOJ.4820.[SDOI2017]硬币游戏(思路 高斯消元 哈希/AC自动机/KMP)

    BZOJ 洛谷 建出AC自动机,每个点向两个儿子连边,可以得到一张有向图.参照 [SDOI2012]走迷宫 可以得到一个\(Tarjan\)+高斯消元的\(O((nm)^3)\)的做法.(理论有\(6 ...

  4. BZOJ 4820 [Sdoi2017]硬币游戏 ——期望DP 高斯消元

    做法太神了,理解不了. 自己想到的是建出AC自动机然后建出矩阵然后求逆计算,感觉可以过$40%$ 用一个状态$N$表示任意一个位置没有匹配成功的概率和. 每种匹配不成功的情况都是等价的. 然后我们强制 ...

  5. bzoj 4820: [Sdoi2017]硬币游戏【kmp+高斯消元】

    有点神,按照1444的做法肯定会挂 注意到它的概率是相同的,所以可以简化状态 详见http://www.cnblogs.com/candy99/p/6701221.html https://www.c ...

  6. 4820: [Sdoi2017]硬币游戏

    4820: [Sdoi2017]硬币游戏 链接 分析: 期望dp+高斯消元. 首先可以建出AC自动机,Xi表示经过节点i的期望次数,然后高斯消元,这样点的个数太多,复杂度太大.但是AC自动机上末尾节点 ...

  7. 【BZOJ4820】[SDOI2017]硬币游戏(高斯消元)

    [BZOJ4820][SDOI2017]硬币游戏(高斯消元) 题面 BZOJ 洛谷 题解 第一眼的感觉就是构\(AC\)自动机之后直接高斯消元算概率,这样子似乎就是\(BZOJ1444\)了.然而点数 ...

  8. [Sdoi2017]硬币游戏 [高斯消元 KMP]

    [Sdoi2017]硬币游戏 题意:硬币序列,H T等概率出现,\(n \le 300\)个人猜了一个长为$ m \le 300$的字符串,出现即获胜游戏结束.求每个人获胜概率 考场用了[1444: ...

  9. BZOJ4820 Sdoi2017 硬币游戏 【概率期望】【高斯消元】【KMP】*

    BZOJ4820 Sdoi2017 硬币游戏 Description 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利.大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实 ...

随机推荐

  1. git 创建本地分支与远程分支

    早上抽空整理了下git常用操作,偶尔看看加深下印象吧: 如果github上已经有master分支 和dev分支 在本地 git checkout -b dev 新建并切换到本地dev分支 git pu ...

  2. react学习(三)之生命周期/refs/受控组件 篇

    挂载/卸载 //在类组件中 class Clock extends React.Component { constructor(props) { super(props); this.state = ...

  3. crontab架构和格式

    crontab架构图 分时日月周*****my command(可以是一个linux命令,也可以是一个脚本文件,可以是shell格式也可以是python格式,也可是java格式.....) 按照格式编 ...

  4. docker限制容器内存使用上限

    记录一个项目开发部署中遇到的一个问题,处理经验总结. 问题: 我们的项目使用的是Angular6 + Spring boot + redis + mycat结构,项目部署在容器里面,项目正式部署以后, ...

  5. wap2app(九)-- 使用mui.previewImage之后,页面a链接不能跳转

    使用Hbuilder的长按保存图片的预览图片之后,页面所有的a链接都不能跳转. 解决办法: 可以使用下面绑定tap利用js跳转,亲测有效. mui('body').on( 'tap' , 'a' , ...

  6. 使用MUI/html5plus集成微信支付需要注意的几点问题

    1)需要在服务器根目录放上证书,从微信开放平台下载 2)客户端组件目录名一定要按照微信要求

  7. L2-024. 部落

    在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈.我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同 ...

  8. ERP主副机和打印机配置FAQ

    一.主副机 1. ERP主副机配置 先配置主机 再配置副机 在主机的电脑上键盘按win+R键,输入cmd 再输入ipconfig查看主机的IP 然后在副机上安装银盒子ERP,在红框位置输入主机IP 选 ...

  9. ES搜索引擎基本操作

    一.创建索引库 执行:,索引库的名称为zhen 结果(我已经创建过了,重复执行会报错): 二.创建索引 1.添加索引 2.添加索引(动态添加新列) 3.添加指定id的索引[根据id可以直接修改一前的索 ...

  10. Android开发--Service和Activity通过广播传递消息

    Android的Service也运行在主线程,但是在服务里面是没法直接调用更改UI,如果需要服务传递消息给Activity,通过广播是其中的一种方法: 一.在服务里面发送广播 通过intent传送数据 ...