[LOJ#2327]「清华集训 2017」福若格斯

试题描述

小d是4xx9小游戏高手。

有一天,小d发现了一个很经典的小游戏:跳青蛙。

游戏在一个 \(5\) 个格子的棋盘上进行。在游戏的一开始,最左边的两个格子上各有一个向右的青蛙,最右边的两个格子上各有一个向左的青蛙。

每次移动可以选取一个青蛙,向这只青蛙的前方移动一格到空格子中或跳过前方的一个不同朝向的青蛙并移动到空格子中。

为了使你更好地理解这个游戏,我们下发了一个游戏demo作为参考(注意:这个demo中的棋盘大小和题目中并不相同)。

这个游戏本身当然难不倒小d,小d轻松地就解决了这个游戏。但是一个人玩游戏实在是太寂寞了,于是小d找到了小m和他一起玩耍。小d规定,自己只能操控向右的青蛙,小m只能操控向左的青蛙。

小d很快发现,这个游戏想要做到双方轮流行动,就无法达到交换所有青蛙的游戏结局。于是,小d打开了 \(m\) 个游戏,并规定双方轮流行动,每次选择其中一个游戏并控制自己的青蛙行动一步(不能不动)。小d发现,这么做的话就能够使大部分的游戏最终都交换所有的青蛙了。

由于小d是坑队友高手,所以他们玩了一会之后,就开始互相坑害对方,都希望使对方无法行动。他们约定,当轮到一方行动时,若其所有的青蛙都无法行动,则对方获得游戏的胜利。正当博弈论大师小d计算着谁会成为最后的胜者时,电脑卡死了。小d发现,只能kill掉一些游戏才能使剩下的游戏进行下去了。由于电脑已经卡死了,小d无法自由选择kill掉哪些游戏,只能运行系统自带的随机kill小程序。具体来说,小d运行这个随机kill小程序之后,每个游戏有 \(\frac{1}{2}\) 的概率被kill掉,有 \(\frac{1}{2}\) 的概率能够继续下去。游戏之间被kill掉的概率是独立的。

小d思考了一番,决定如果运行小程序之后他的胜率过低,就直接重启电脑。这时,小d突然发现自己已经不记得刚才轮到谁行动了,于是他决定综合考虑自己先手和后手的胜率。

小d并不擅长概率论,他想让你告诉他运行小程序后,剩下的局面为小d必胜、小m必胜、先手必胜、后手必胜的概率各为多少,这样他才能更好地决定是否重启电脑。

为了避免精度问题,输出答案乘 \(2^m\) 之后对 \(998244353\) 取模的结果。

注意:题目并不保证输入的所有m个状态中小m和小d之前的总行动步数相差不超过 \(1\),但是保证不会出现起始状态无法到达的状态。

输入

从标准输入读入数据。

我们使用一个长度为5的字符串来表示一个状态,其中,L 表示面朝右的青蛙,R 表示面朝左的青蛙,_(下划线)表示空格子。例如,初始状态为 LL_RR

本题含有多组数据,第一行两个整数 \(T,C(1\leq C\leq 100)\) 分别表示测试点编号和数据组数。

对于每组数据,第一行一个整数 \(n(1\leq n\leq 23)\) 表示不同状态的棋盘个数,接下来 \(n\) 行每行一个长度为 \(5\) 的字符串 \(s_i\) 和一个正整数 \(a_i(1\leq a_i\leq 10^6)\),分别表示棋盘的状态和在该状态下的棋盘的个数。

保证输入的字符串合法且不重复。

输出

输出到标准输出。

定义 \(m = \sum a_i\)。

对于每组数据,输出一行四个整数,分别表示小d必胜(即L的控制方必胜)、小m必胜(即R的控制方必胜)、先手必胜、后手必胜的概率乘 \(2^m\) 之后对 \(998244353\) 取模的结果。

输入示例1

0 1
1
LL_RR 1

输出示例1

0 0 1 1

输入示例2

0 1
3
LRRL_ 1
LRR_L 1
LLR_R 1

输出示例2

4 2 0 2

输入示例3

0 1
1
LRRL_ 1000000

输出示例3

421273116 0 0 1

数据规模及约定

对于 \(100\%\) 的数据,\(1\leq n\leq 23,1\leq m\leq 10^6,1\leq C\leq 100\),所有可能出现在该数据点的状态均为等概率出现(也就是说你可以认为最后一个点中每种状态的 \(a_i\) 之和大约为 \(10^8/23\))。

题解

其实这题我也没搞透。。。不知道 surreal number 会在什么情况下适用。。。

题解在这里

放个链接:点我!

代码中 Creal()Cfake() 两个函数用到了一个结论:

\(\sum_{i=0}^{ \mathrm{min} \{a, b-k\} } { C_a^i \cdot C_b^{i+k} } = C_{a+b}^{a+k}\)

证明的话可以考虑两种不同的球,黑球和白球分别有 \(a\) 和 \(b\) 个,然后我们选择 \(i\) 个白球和 \(i+k\) 个黑球并将它们反色,然后搁回去,那么新序列中就是 \(a+b\) 个位置中选择 \(a+k\) 个位置放白球,得到了等式的右边。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <string>
#include <map>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--) int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
} #define maxn 25
#define maxm 2000010
#define MOD 998244353
#define oo 2147483647
#define LL long long map <string, int> val;
map <int, int> tot; int search(string s) {
if(val.count(s)) return val[s];
int L = oo, R = oo;
rep(i, 0, 4) {
string to;
if(s[i] == 'L') {
to = s;
if(i <= 3 && to[i+1] == '_') swap(to[i], to[i+1]), L = search(to);
to = s;
if(i <= 2 && to[i+1] == 'R' && to[i+2] == '_') swap(to[i], to[i+2]), L = search(to);
}
if(s[i] == 'R') {
to = s;
if(i >= 1 && to[i-1] == '_') swap(to[i], to[i-1]), R = search(to);
to = s;
if(i >= 2 && to[i-1] == 'L' && to[i-2] == '_') swap(to[i], to[i-2]), R = search(to);
}
}
int now = 0;
if(L == 0 && R == oo) now = 2;
if(L == oo && R == 0) now = -2;
if(L == 0 && R == 2) now = 1;
if(L == -2 && R == 0) now = -1;
// 3 up, -3 down, 4 star.
if(L == 0 && R == 4) now = 3;
if(L == 4 && R == 0) now = -3;
if((L == R && R == 0) || (L == 3 && R == -3)) now = 4;
// These are the states that can be found in the solution.(See the state-tree)
return val[s] = now;
} char str[maxn]; int Pow(int a, int b) {
int ans = 1, t = a;
while(b) {
if(b & 1) ans = (LL)ans * t % MOD;
t = (LL)t * t % MOD; b >>= 1;
}
return ans;
} int fac[maxm], inv[maxm];
LL C(int n, int m) { return (LL)fac[n] * inv[m] % MOD * inv[n-m] % MOD; } LL t_1, t0, t1, A[maxm];
void Creal(int s_2, int s_1, int s1, int s2) {
t_1 = t0 = t1 = 0;
rep(i, 0, s_1 + s1) A[i] = C(s_1 + s1, i);
rep(i, 1, s_1 + s1) (A[i] += A[i-1]) %= MOD;
rep(i, 0, s_2 + s2) {
int k = (i - s_2 << 1);
LL now = C(s_2 + s2, i);
if(k < -s1) t_1 += now * A[s_1+s1] % MOD; // all negative
else if(k > s_1) t1 += now * A[s_1+s1] % MOD; // all positive
else {
t0 += now * (A[s_1-k] - (s_1 - k == 0 ? 0 : A[s_1-k-1]) + MOD) % MOD;
t_1 += now * (s_1 - k == 0 ? 0 : A[s_1-k-1]) % MOD;
t1 += now * (A[s_1+s1] - A[s_1-k] + MOD) % MOD;
}
}
t_1 %= MOD; t0 %= MOD; t1 %= MOD;
return ;
}
LL f_2, f_1, f0, f1, f2;
void Cfake(int s_1, int s1) {
f_2 = f_1 = f0 = f1 = f2 = 0;
rep(i, 0, s_1 + s1) {
LL now = C(s_1 + s1, i);
if(i < s_1 - 1) f_2 += now;
if(i == s_1 - 1) f_1 += now;
if(i == s_1) f0 += now;
if(i == s_1 + 1) f1 += now;
if(i > s_1 + 1) f2 += now;
}
f_2 %= MOD; f_1 %= MOD; f0 %= MOD; f1 %= MOD; f2 %= MOD;
return ;
} int main() {
search(string("LL_RR")); fac[0] = inv[0] = 1;
rep(i, 1, maxm - 1) fac[i] = (LL)fac[i-1] * i % MOD, inv[i] = (LL)inv[i-1] * Pow(i, MOD - 2) % MOD; read(); int T = read();
while(T--) {
int n = read();
rep(i, -3, 4) tot[i] = 0;
rep(i, 1, n) scanf("%s", str), tot[val[string(str)]] += read();
Creal(tot[-2], tot[-1], tot[1], tot[2]);
Cfake(tot[-3], tot[3]);
/*
s0 and s1 are used to separate the imaginary(fake) part.
s0: number of star is even
s1: number of star is odd
Only when star doesn't exist(or appears even number of times)
situation abs(Up - Down) = 1 can be count in "L wins" or "R wins".
*/
LL s0 = 1, s1 = 0, Lw = 0, Rw = 0, Fw = 0, Fl = 0;
if(tot[4]) s0 = s1 = Pow(2, tot[4] - 1);
// Real part determims who will win.
Lw += t1 * Pow(2, tot[-3] + tot[4] + tot[3]) % MOD;
Rw += t_1 * Pow(2, tot[-3] + tot[4] + tot[3]) % MOD;
// When Real part is equal(total sum is 0)
Lw += t0 * s0 % MOD * (f1 + f2) % MOD; // Up more, then L wins.
Rw += t0 * s0 % MOD * (f_1 + f_2) % MOD; // Down more, then R wins.
Fl += t0 * s0 % MOD * f0 % MOD; // First lose.(state 0, this state includes (or equally) nothing)
Lw += t0 * s1 % MOD * f2 % MOD; // (Up - Down) more than one, then L wins.
Rw += t0 * s1 % MOD * f_2 % MOD; // (Down - Up) more than one, then R wins.
Fw += t0 * s1 % MOD * (f1 + f_1 + f0) % MOD; // First win.
Lw %= MOD; Rw %= MOD; Fw %= MOD; Fl %= MOD;
// At last, everybody multiply state 0 because we ignored it before.
(Lw *= Pow(2, tot[0])) %= MOD;
(Rw *= Pow(2, tot[0])) %= MOD;
(Fw *= Pow(2, tot[0])) %= MOD;
(Fl *= Pow(2, tot[0])) %= MOD;
printf("%lld %lld %lld %lld\n", Lw, Rw, Fw, Fl);
} return 0;
}

[LOJ#2327]「清华集训 2017」福若格斯的更多相关文章

  1. LOJ2327 「清华集训 2017」福若格斯 【不平等博弈】

    题目链接:LOJ 对于这道题,我们要分3步来做它. 什么是 Surreal Number 及如何解决博弈问题. 如何用 Surreal Number 解决这道题. 推出结论之后如何计数 首先看看这篇文 ...

  2. Loj #2331. 「清华集训 2017」某位歌姬的故事

    Loj #2331. 「清华集训 2017」某位歌姬的故事 IA 是一名会唱歌的女孩子. IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符, ...

  3. Loj #2324. 「清华集训 2017」小 Y 和二叉树

    Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...

  4. Loj #2321. 「清华集训 2017」无限之环

    Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...

  5. Loj 2320.「清华集训 2017」生成树计数

    Loj 2320.「清华集训 2017」生成树计数 题目描述 在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\ ...

  6. [LOJ#2330]「清华集训 2017」榕树之心

    [LOJ#2330]「清华集训 2017」榕树之心 试题描述 深秋.冷风吹散了最后一丝夏日的暑气,也吹落了榕树脚下灌木丛的叶子.相识数年的Evan和Lyra再次回到了小时候见面的茂盛榕树之下.小溪依旧 ...

  7. [LOJ#2329]「清华集训 2017」我的生命已如风中残烛

    [LOJ#2329]「清华集训 2017」我的生命已如风中残烛 试题描述 九条可怜是一个贪玩的女孩子. 这天她在一堵墙钉了 \(n\) 个钉子,第 \(i\) 个钉子的坐标是 \((x_i,y_i)\ ...

  8. [LOJ#2328]「清华集训 2017」避难所

    [LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所 ...

  9. [LOJ#2326]「清华集训 2017」简单数据结构

    [LOJ#2326]「清华集训 2017」简单数据结构 试题描述 参加完IOI2018之后就是姚班面试.而你,由于讨厌物理.并且想成为乔布斯一样的创业家,被成功踢回贵系. 转眼,时间的指针被指向201 ...

随机推荐

  1. numpy.random.shuffle(x)的用法

    numpy.random.shuffle(x) Modify a sequence in-place by shuffling its contents. Parameters: x : array_ ...

  2. python查看安装包

    D:\Python27\Scripts>pip listbackports.ssl-match-hostname (3.4.0.2)basicauth (0.2)certifi (14.5.14 ...

  3. leetcode笔记(一)309. Best Time to Buy and Sell Stock with Cooldown

    题目描述 (原题目链接) Say you have an array for which the ith element is the price of a given stock on day i. ...

  4. 两种方法实现text输入框中“请输入关键字”的提醒

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Java 替换word文档文字,指定位置插入图片

    先说下 需要的依赖包 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ex ...

  6. 三、MySQL PHP 语法

    MySQL PHP 语法 MySQL 可应用于多种语言,包括 PERL, C, C++, JAVA 和 PHP. 在这些语言中,Mysql在PHP的web开发中是应用最广泛. 在本教程中我们大部分实例 ...

  7. FreeRTOS的学习路线

    背景 由于之前接触过一些嵌入式RTOS,如Keil-RTX,uCOS-II,也曾经关注过FreeRTOS,但一直没有机会采用FreeRTOS开发.目前FreeRTOS做为主流RTOS,风声正盛.作为嵌 ...

  8. 懒人的mysql管理脚本

    最近常用到的命令,太懒不想打太多 1,mysql启动,重启,停止脚本 echo '/usr/local/mysql5/support-files/mysql.server $1'>>/us ...

  9. SQL Server字符串聚合拼接办法

    数据范例如下: 要得到的结果目标,获取type相同的所有names拼接在一起的字符串: SqlServer并没有一个直接拼接字符串的函数,下面所提到的方法,只是日常的开发中自己个人用到的一些思路,仅供 ...

  10. 【CSS】CSS 的优先级总结

    样式的优先级 多重样式(Multiple Styles):如果外部样式.内部样式和内联样式同时应用于同一个元素,就是使多重样式的情况. 一般情况下,优先级如下: (外部样式)External styl ...