题目传送门

Description

周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利。

大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了。

同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况。

用 $ \texttt{H} $ 表示正面朝上, 用 $ \texttt{T} $ 表示反面朝上,扔很多次硬币后,会得到一个硬币序列。比如 $ \texttt{HTT} $ 表示第一次正面朝上,后两次反面朝上。

但扔到什么时候停止呢?大家提议,选出 $ n $ 个同学, 每个同学猜一个长度为 $ m $ 的序列,当某一个同学猜的序列在硬币序列中出现时,就不再扔硬币了,并且这个同学胜利。为了保证只有一个同学胜利,同学们猜的 $ n $ 个序列两两不同。

很快,$ n $个同学猜好序列,然后进入了紧张而又刺激的扔硬币环节。你想知道,如果硬币正反面朝上的概率相同,每个同学胜利的概率是多少。

对于 $ 10% $ 的数据,$ 1 \leq n, m \leq 3 $;

对于 $ 40% $ 的数据,$ 1 \leq n, m \leq 18 $;

对于另外 $ 20%$ 的数据,$ n = 2 $;

对于 $ 100% $ 的数据,$ 1 \leq n, m \leq 300 $。

Solution

考虑使用概率生成函数。

我们设 \([x^n]G(x)\) 表示在 \(n\) 次操作后仍未结束的概率,\([x^n]F_i(x)\) 表示在 \(n\) 次操作后第 \(i\) 个人获胜的概率,\(a_{i,j,k}\) 表示第 \(i\) 个人的 \([1,k]\) 与第 \(j\) 个人的 \([m-k+1,m]\) 是否相同。

那我们可以得到:

\[G(x)+\sum_{i=1}^{n} F_i(x)=1+x·G(x)
\]
\[G(x)(\frac{1}{2})^mx^m=\sum_{j=1}^{n}\sum_{k=1}^{m}a_{i,j,k}(\frac{1}{2})^{m-k}x^{m-k}F_j(x)
\]

因为你最后只需要求出 \(F_i(1)\) ,所以可以把 \(x\) 都当成 \(1\) ,然后第一个式子就变为了:

\[\sum_{i=1}^{n} F_i(1)=1
\]

(似乎很显然的不需要第一个式子)

然后你就有 \(n+1\) 个方程,可以直接高斯消元了。

Code

#include <bits/stdc++.h>

using namespace std;

#define Int register int

#define MAXN 305

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}

template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}

template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}

template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}

int n,m;

char S[MAXN][MAXN];

bool a[MAXN][MAXN][MAXN];

double mat[MAXN][MAXN],pw2[MAXN],ans[MAXN];

#define seed 1000000007

#define ull unsigned long long

ull pw[MAXN],pre[MAXN][MAXN],suf[MAXN][MAXN];

void gauss (){

	int up = n + 1;

	for (Int i = 1;i <= up;++ i){

		int ind = i;

		for (Int j = i;j <= up;++ j) if (abs (mat[j][i]) >= 1e-7){ind = j;break;}

		if (ind ^ i) swap (mat[i],mat[ind]);

		for (Int j = i + 1;j <= up;++ j){

			double det = mat[j][i] / mat[i][i];

			for (Int k = i;k <= up + 1;++ k) mat[j][k] -= mat[i][k] * det;

		}

	}

	for (Int i = up;i >= 0;-- i){

		for (Int j = i + 1;j <= up;++ j) mat[i][up + 1] -= mat[i][j] * ans[j];

		ans[i] = mat[i][up + 1] / mat[i][i];

	}

}

signed main (){

	read (n,m),pw[0] = pw2[0] = 1;

	for (Int i = 1;i <= m;++ i) pw[i] = pw[i - 1] * seed,pw2[i] = pw2[i - 1] * 2;

	for (Int i = 1;i <= n;++ i) scanf ("%s",S[i] + 1);

	for (Int i = 1;i <= n;++ i){

		for (Int j = 1;j <= m;++ j) pre[i][j] = pre[i][j - 1] * seed + (S[i][j] == 'T');

		for (Int j = m;j >= 1;-- j) suf[i][j] = suf[i][j + 1] + (S[i][j] == 'T') * pw[m - j];

	}

	for (Int i = 1;i <= n;++ i) 

		for (Int j = 1;j <= n;++ j)

			for (Int k = 1;k <= m;++ k)

				a[i][j][k] = (pre[i][k] == suf[j][m - k + 1]);

	for (Int i = 1;i <= n;++ i){

		for (Int j = 1;j <= n;++ j)

			for (Int k = 1;k <= m;++ k)

				if (a[i][j][k]) mat[i][j] += pw2[k];

		mat[i][n + 1] = -1;

	}

	for (Int i = 1;i <= n;++ i) mat[n + 1][i] = 1;mat[n + 1][n + 2] = 1;

	gauss ();for (Int i = 1;i <= n;++ i) printf ("%.6f\n",ans[i]);

	return 0;

}

题解 「SDOI2017」硬币游戏的更多相关文章

  1. @loj - 2004@ 「SDOI2017」硬币游戏

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数 ...

  2. 【LOJ 2004】「SDOI2017」硬币游戏

    LOJ 2004 100pts 首先我们肯定要建AC自动机的.. 那么这题就肯定是个AC自动机上\(dp\). 所以想想状态. 首先如果我们把状态设成这样行不行: \(dp(i)\)表示匹配到了i节点 ...

  3. 「SDOI2017」硬币游戏

    题目链接 问题分析 首先一个显然的做法就是建出AC自动机,然后高斯消元.但是这样的复杂度是\(O(n^3m^3)\)的. 我们发现其实只需要求AC自动机上\(n\)个状态的概率,而其余的概率是没有用的 ...

  4. 【LOJ】#2067. 「SDOI2016」硬币游戏

    题解 c一样的就是一个独立的游戏 我们对于2和3的指数 sg[i][j] 表示\(c \cdot 2^i \cdot 3^j\)的棋子,只有这个硬币是反面,翻转的硬币是正面的sg值 枚举sg函数所有可 ...

  5. 题解 「HDU6403」卡片游戏

    link Description 桌面上摊开着一些卡牌,这是她平时很爱玩的一个游戏.如今卡牌还在,她却不在我身边.不知不觉,我翻开了卡牌,回忆起了当时一起玩卡牌的那段时间. 每张卡牌的正面与反面都各有 ...

  6. 「SDOI2017」树点涂色 解题报告

    「SDOI2017」树点涂色 我sb的不行了 其实一开始有一个类似动态dp的想法 每个点维护到lct树上到最浅点的颜色段数,然后维护一个\(mx_{0,1}\)也就是是否用虚儿子的最大颜色 用个set ...

  7. LibreOJ 2003. 「SDOI2017」新生舞会 基础01分数规划 最大权匹配

    #2003. 「SDOI2017」新生舞会 内存限制:256 MiB时间限制:1500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   题目描述 ...

  8. AC日记——「SDOI2017」序列计数 LibreOJ 2002

    「SDOI2017」序列计数 思路: 矩阵快速幂: 代码: #include <bits/stdc++.h> using namespace std; #define mod 201704 ...

  9. 【LOJ】#2269. 「SDOI2017」切树游戏

    题解 把所有的数组一开始就FWT好然后再IFWT回去可以减小常数 从13s跑到0.7s-- 可以参照immortalCO的论文,感受一下毒瘤的动态动态DP 就是用数据结构维护线性递推的矩阵的乘积 由于 ...

随机推荐

  1. Ubuntu16.04 + OpenCV源码 + Qt5.10 安装、配置

    在VMWare中配置安装Ubuntu16.04.没有什么特别的地方,正常安装即可. 安装最新版qt,此时5.10.按照普通QT教程安装(需要勾选gcc),无须sudo,此时不用管OpenCV.地址:h ...

  2. MySQL高可用主从复制部署

    原文转自:https://www.cnblogs.com/itzgr/p/10233932.html作者:木二 目录 一 基础环境 二 实际部署 2.1 安装MySQL 2.2 初始化MySQL 2. ...

  3. JavaScript高级程序设计学习笔记之事件

    1.事件流 事件流描述的是从页面中接收事件的顺序. 事件冒泡 IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播 ...

  4. JS 之 每日一题 之 算法 ( 划分字母区间 )

    题目详解: 字符串 S 由小写字母组成.我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段.返回一个表示每个字符串片段的长度的列表. 例子: 示例 1: 输入:S = &quo ...

  5. (一)羽夏看C语言——简述

    "羽夏看C语言"介绍什么   本系列从汇编的角度,比较翔实的介绍C语言.C++和C其实是一样的东西,C++的编译器只是更强大,更能帮助我们写代码,例如模板.没有特殊说明,本系列不会 ...

  6. 并发编程之:JUC并发控制工具

    大家好,我是小黑,一个在互联网苟且偷生的农民工. 在上一期我们讲了Thread.join()方法和CountDownLatch,这两者都可以做到等待一个线程执行完毕之后当前线程继续执行,并且Count ...

  7. Flask(3)- Flask 中的 HTTP 方法

    查看 app.route() 源代码 def route(self, rule: str, **options: t.Any) -> t.Callable: """ ...

  8. GIT:修改上一次提交的注释信息(git commit --amend)

    git commit -m 注释信息 如果这时候注释信息输入错误,就可以输入以下指令更改 git commit --amend 键入" i "进入编辑模式 修改后键入ESC,:wq ...

  9. SpringSecurity-Shiro-初见

    目录 简介 实战环境搭建 SpringSecurity 认证和授权 权限控制和注销 记住我 Shiro 快速上手 shiro整合mybais 简介 在 Web 开发中,安全一直是非常重要的一个方面. ...

  10. 【Azure API 管理】APIM 配置Validate-JWT策略,验证RS256非对称(公钥/私钥)加密的Token

    问题描述 在APIM中配置对传入的Token进行预验证,确保传入后端被保护的API的Authorization信息正确有效,可以使用validate-jwt策略.validate-jwt 策略强制要求 ...