题目链接:LOJ

对于这道题,我们要分3步来做它。

  1. 什么是 Surreal Number 及如何解决博弈问题。
  2. 如何用 Surreal Number 解决这道题。
  3. 推出结论之后如何计数

首先看看这篇文章(如果真要看那本书的话会耗死你)

然后我们来总结总结。

首先,博弈局面可以对应到超现实数,它的左集是\(\text{L}\)走一步的局面,右集是\(\text{R}\)走一步的局面,用\(\text{L}\)是否“赢得更爽”来判定大小,显然左集的数都小于它,它也小于右集的数(因为多走一步就是更接近无法行动)

那游戏的相反数是什么呢?显然就是将\(\text{L}\)和\(\text{R}\)玩家调换,然后就可以了。

还有游戏的和。我们知道游戏的和是两个游戏可以选一个走,那么

\[x+y=\{(x_L+y)\cup(x+y_L)|(x_R+y)\cup(x+y_R)\}
\]

我们发现它满足普通\(+\)的一堆运算律。所以它就是超现实数的加法了。

至于乘法,这东西对博弈游戏没有用,不管了。

然后我们发现,正数表示\(\text{L}\)必胜,负数表示\(R\)必胜,\(0\)表示后手必胜。现在已经推出了一个结论,于是你开始手玩这道题的23个局面。

但是你发现还有一种新东西叫做\(*=\{0|0\}\),它是一个先手必胜的状态,并且还有两个东西叫做\(\uparrow=\{0|*\}\)和\(\downarrow=\{*|0\}\)。用这些东西推推可以得到一些结论。

  1. \(*,\uparrow,\downarrow\)加上一个正数都是\(\text{L}\)必胜,加上一个负数都是\(\text{R}\)必胜。换句话说,非0数对这三个东西是绝对的(自己造了个词)
  2. \(*+*=0\),所以偶数个\(*\)会互相抵消。
  3. \(\uparrow>0,\downarrow<0,\uparrow+\downarrow=0\),所以\(\uparrow\)和\(\downarrow\)可以互相抵消。但是要注意\(*=\{\downarrow|\uparrow\}\)。
  4. \(*+\uparrow+\uparrow>0,*+\downarrow+\downarrow<0\),而\(*+\uparrow,*+\downarrow\)都是先手赢的局面。也就是说,\(2\uparrow>*>\uparrow>0\),而\(\downarrow\)同理(注意\(*​\)是没有符号的)

于是你根据这些东西,就可以讨论出以下的结论(以下将\(\downarrow\)视作\(-\uparrow\)):

  1. 若\(*\)个数为偶数

    1. 若\(\sum 数字>0\or(\sum 数字=0\and \sum\uparrow>0)\),则\(\text{L}\)胜。

    2. 若\(\sum 数字=0\and \sum\uparrow=0​\),则后手胜。

    3. 若\(\sum 数字<0\or(\sum 数字=0\and\sum\uparrow<0)\),则\(\text{R}\)胜。

  2. 若\(*\)个数为奇数

    1. 若\(\sum 数字>0\and(\sum 数字=0\and\sum\uparrow>1)\),则\(\text{L}\)胜。
    2. 若\(\sum 数字=0\and |\sum\uparrow|\le 1\),则先手胜
    3. 若\(\sum 数字<0\or (\sum 数字=0\and\sum\uparrow<-1)\),则\(\text{R}\)胜。

还有这个游戏的局面

然后你发现,它是求\(2^m\)种情况中四种情况的和。所以还要考虑如何计数。

首先\(\text{num},*,\uparrow\)是相互独立的,所以你可以求出有多少种情况满足\(\sum \text{num}\)大于\(0\),小于\(0\)和等于\(0\),求出满足\(\sum \uparrow\)大于\(1\),小于\(-1\)和\([-1,1]\)的情况。

上面两个东西是同理的,所以只说前一个,设\(a,b,c,d\)表示\(-1,-\frac{1}{2},\frac{1}{2},1\)的数量,那么就是求

\[(1+x^{-2})^a(1+x^{-1})^b(1+x)^c(1+x^2)^d=\frac{(1+x)^{b+c}(1+x^2)^{a+d}}{x^{2a+b}}
\]

的次数\(>0\),\(<0\),\(=0\)对应的系数。首先把\((1+x)^{b+c}\)按照二项式定理展开,然后枚举\((1+x^2)^{b+d}\)的每一项,你发现\(>0\)和\(<0\)对应的是\((1+x)^{b+c}\)的两段。用前缀和搞搞就可以了。

最后合并就是用乘法原理搞搞就可以了。

具体可以看这个4KB多的代码。

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = 2000003, mod = 998244353;
int Id, T, n, cnt[8], fac[N], inv[N], x[N];
inline void upd(int &a, int b){a += b; if(a >= mod) a -= mod;}
inline int sub(int a, int b){return (a < b) ? (a - b + mod) : (a - b);}
inline int add(int a, int b){return (a + b >= mod) ? (a + b - mod) : (a + b);}
inline int kasumi(int a, int b){
int res = 1;
while(b){
if(b & 1) res = (LL) res * a % mod;
a = (LL) a * a % mod; b >>= 1;
}
return res;
}
inline void init(int m){
fac[0] = 1;
for(Rint i = 1;i <= m;i ++) fac[i] = (LL) fac[i - 1] * i % mod;
inv[m] = kasumi(fac[m], mod - 2);
for(Rint i = m;i;i --) inv[i - 1] = (LL) inv[i] * i % mod;
}
inline int C(int n, int m){
if(n < 0 || m < 0 || n < m) return 0;
return (LL) fac[n] * inv[m] % mod * inv[n - m] % mod;
}
/*0-4 : -1~1,5:*, 6:up, 7:down */
int main(){
#ifdef NTFOrzs
freopen("a.in", "r", stdin);
#endif
scanf("%d%d", &Id, &T); init(2e6);
while(T --){
memset(cnt, 0, sizeof cnt);
scanf("%d", &n);
for(Rint i = 1;i <= n;i ++){
char opt[10]; int x;
scanf("%s%d", opt, &x);
if (!strcmp(opt, "LL_RR")) cnt[5] += x;
else if (!strcmp(opt, "L_LRR")) cnt[6] += x;
else if (!strcmp(opt, "LLR_R")) cnt[7] += x;
else if (!strcmp(opt, "LRL_R")) cnt[5] += x;
else if (!strcmp(opt, "L_RLR")) cnt[5] += x;
else if (!strcmp(opt, "_RLLR")) cnt[0] += x;
else if (!strcmp(opt, "LRRL_")) cnt[4] += x;
else if (!strcmp(opt, "LR_RL")) cnt[1] += x;
else if (!strcmp(opt, "RL_LR")) cnt[3] += x;
else if (!strcmp(opt, "_RLRL")) cnt[0] += x;
else if (!strcmp(opt, "RLRL_")) cnt[4] += x;
else if (!strcmp(opt, "RRL_L")) cnt[4] += x;
else if (!strcmp(opt, "R_RLL")) cnt[0] += x;
else cnt[2] += x;
}
#ifdef NTFOrzs
printf("cnt[0] = %d, cnt[1] = %d, cnt[2] = %d, cnt[3] = %d, cnt[4] = %d, cnt[5] = %d, cnt[6] = %d, cnt[7] = %d\n", cnt[0], cnt[1], cnt[2], cnt[3], cnt[4], cnt[5], cnt[6], cnt[7]);
#endif
for(Rint i = 0;i <= cnt[1] + cnt[3];i ++) x[i] = C(cnt[1] + cnt[3], i);
for(Rint i = 1;i <= cnt[1] + cnt[3];i ++) upd(x[i], x[i - 1]);
int res1 = 0, res2 = 0, res3 = 0;
for(Rint i = 0;i <= cnt[0] + cnt[4];i ++){
int tmp = C(cnt[0] + cnt[4], i), t2 = 2 * (cnt[0] - i) + cnt[1];
upd(res2, (LL) tmp * C(cnt[1] + cnt[3], t2) % mod);
if(t2 >= 1) upd(res1, (LL) tmp * x[min(t2 - 1, cnt[1] + cnt[3])] % mod);
if(t2 >= 0 && t2 <= cnt[1] + cnt[3]) upd(res3, (LL) tmp * sub(x[cnt[1] + cnt[3]], x[t2]) % mod);
else if(t2 < 0) upd(res3, (LL) tmp * x[cnt[1] + cnt[3]] % mod);
}
#ifdef NTFOrzs
printf("%d %d %d\n", res1, res2, res3);
#endif
int ans0 = 0, ans1 = 0, ans2 = 0, ans3 = 0, tmp1 = 0, tmp2 = 0, tmp3 = 0, tmp4 = 0, tmp5 = 0;
for(Rint i = 0;i <= cnt[7];i ++) x[i] = C(cnt[7], i);
for(Rint i = 1;i <= cnt[7];i ++) upd(x[i], x[i - 1]);
for(Rint i = 0;i <= cnt[6];i ++){
int tmp = C(cnt[6], i);
upd(tmp3, (LL) tmp * C(cnt[7], i) % mod);
upd(tmp2, (LL) tmp * C(cnt[7], i + 1) % mod);
upd(tmp4, (LL) tmp * C(cnt[7], i - 1) % mod);
if(i >= 2) upd(tmp5, (LL) tmp * x[min(i - 2, cnt[7])] % mod);
if(i <= cnt[7] - 2) upd(tmp1, (LL) tmp * sub(x[cnt[7]], x[i + 1]) % mod);
}
#ifdef NTFOrzs
printf("%d %d %d %d %d\n", tmp1, tmp2, tmp3, tmp4, tmp5);
#endif
upd(ans0, add(tmp4, tmp5)); upd(ans1, add(tmp1, tmp2)); upd(ans3, tmp3);
if(cnt[5] > 0){
upd(ans0, tmp5); upd(ans1, tmp1); upd(ans2, add(tmp2, add(tmp3, tmp4)));
#ifdef NTFOrzs
printf("%d %d %d %d\n", ans0, ans1, ans2, ans3);
#endif
int tmp = kasumi(2, cnt[5] - 1);
ans0 = (LL) ans0 * tmp % mod;
ans1 = (LL) ans1 * tmp % mod;
ans2 = (LL) ans2 * tmp % mod;
ans3 = (LL) ans3 * tmp % mod;
}
ans0 = (LL) ans0 * res2 % mod * kasumi(2, cnt[2]) % mod;
ans1 = (LL) ans1 * res2 % mod * kasumi(2, cnt[2]) % mod;
ans2 = (LL) ans2 * res2 % mod * kasumi(2, cnt[2]) % mod;
ans3 = (LL) ans3 * res2 % mod * kasumi(2, cnt[2]) % mod;
upd(ans0, (LL) res3 * kasumi(2, cnt[2] + cnt[5] + cnt[6] + cnt[7]) % mod);
upd(ans1, (LL) res1 * kasumi(2, cnt[2] + cnt[5] + cnt[6] + cnt[7]) % mod);
printf("%d %d %d %d\n", ans0, ans1, ans2, ans3);
}
}

LOJ2327 「清华集训 2017」福若格斯 【不平等博弈】的更多相关文章

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

    [LOJ#2327]「清华集训 2017」福若格斯 试题描述 小d是4xx9小游戏高手. 有一天,小d发现了一个很经典的小游戏:跳青蛙. 游戏在一个 \(5\) 个格子的棋盘上进行.在游戏的一开始,最 ...

  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 #2325. 「清华集训 2017」小Y和恐怖的奴隶主

    #2325. 「清华集训 2017」小Y和恐怖的奴隶主 内存限制:256 MiB时间限制:2000 ms标准输入输出 题目类型:传统评测方式:文本比较   题目描述 "A fight? Co ...

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

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

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

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

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

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

随机推荐

  1. golang--获取进程ID(windows)

    package main import ( "fmt" "strconv" "syscall" "unsafe" ) t ...

  2. 自建Git服务Gogs和CI/CD服务Drone

    自建Git服务Gogs和CI/CD服务Drone 项目:https://gogs.io Gogs运行 docker run -d --name=gogs -p 10022:22 -p 10088:30 ...

  3. 【.Net Core】编译时禁止自动生成netcoreapp文件夹

    原文:[.Net Core]编译时禁止自动生成netcoreapp文件夹 每次在编译生成文件时,VS都会自动在<OutputPath>属性指定的路劲后再追加一个用NetCore命名的文件夹 ...

  4. netcore 版本升级 导致的cookie验证失败

    排查了两天的问题,本来都是运行正常的cookie验证,突然不好用了,服务器获取不到cookie信息. 我确实是升级了.netcore sdk,之前是2.2.102,后来升级成了2.2.107,一开始并 ...

  5. java之hibernate之基于外键的一对一单向关联映射

    这篇讲解基于外键的一对一单向关联映射 1.考察如下信息,人和身份证之间是一个一对一的关系.表的设计 注意:基于外键的一对一关联的表结构和多对一的表结构是一致的,但是,外键是唯一的. 2.类的结构 Pe ...

  6. asp.net core 一个中小型项目实战的起手式——Swagger配置

    交流群:863563315 一.Swagger是什么 Swagger 是一款RESTFUL接口的.基于YAML.JSON语言的文档在线自动生成.代码自动生成的工具. 二.如何在项目中加入Swagger ...

  7. Winform 使用热键功能实现Ctrl+C和Ctrl+V复制粘贴功能

    当我们使用winform控件的时候,会发现这些控件(比如Label)不支持Ctrl+c 复制和Ctrl+v 快捷键复制粘贴功能,如果我们需要实现这个功能改怎么做呢? 1. 首先我们创建一个winfor ...

  8. Java调用WebService方法总结(9,end)--Http方式调用WebService

    Http方式调用WebService,直接发送soap消息到服务端,然后自己解析服务端返回的结果,这种方式比较简单粗暴,也很好用:soap消息可以通过SoapUI来生成,也很方便.文中所使用到的软件版 ...

  9. python day 8: re模块补充,导入模块,hashlib模块,字符串格式化,模块知识拾遗,requests模块初识

    目录 python day 8 1. re模块补充 2. import模块导入 3. os模块 4. hashlib模块 5. 字符串格式:百分号法与format方法 6. 模块知识拾遗 7. req ...

  10. Linux多IP配置

    # ifconfig eth0:1 172.168.1.222