[BZOJ3729]Gty的游戏
[BZOJ3729]Gty的游戏
试题描述
某一天gty在与他的妹子玩游戏。
妹子提出一个游戏,给定一棵有根树,每个节点有一些石子,每次可以将不多于L的石子移动到父节点,询问
将某个节点的子树中的石子移动到这个节点先手是否有必胜策略。
gty很快计算出了策略。
但gty的妹子十分机智,她决定修改某个节点的石子或加入某个新节点。
gty不忍心打击妹子,所以他将这个问题交给了你。
另外由于gty十分绅士,所以他将先手让给了妹子。
输入
第一行两个数字,n和L,n<=5*10^4,L<=10^9
第二行n个数字,表示每个节点初始石子数。
接下来n-1行,每行两个整数u和v,表示有一条从u到v的边。
接下来一行一个数m,表示m组操作。
接下来m行,每行第一个数字表示操作类型
若为1,后跟一个数字v,表示询问在v的子树中做游戏先手是否必胜。
若为2,后跟两个数字x,y表示将节点x的石子数修改为y。
若为3,后跟三个数字u,v,x,表示为u节点添加一个儿子v,初始石子数为x。
在任意时刻,节点数不超过5*10^4。
输出
对于每个询问,若先手必胜,输出"MeiZ",否则输出"GTY"。
另,数据进行了强制在线处理,对于m组操作,除了类型名以外,都需要异或之前回答为"MeiZ"的个数。
输入示例
输出示例
GTY
数据规模及约定
见“输入”
题解
首先对于每个节点上石子的个数我们可以对 L + 1 取模,因为每次只能取不超过 L 个。考虑一场新的博弈:有一堆石子,大小为 x,每次可以取 1 ~ L 个石子,最终无法取石子的人输。那么显然当 x <= L 时,先手必胜;x = L + 1 时,先手必输;然后就可以推出 x % (L + 1) = 0 时先手必输,否则先手必赢(想一想,为什么)。
然后发现如果假定当前子树根节点深度为 0,那么深度为偶数的节点上石子可以忽略,因为假设 A 先将偶数层上某节点的 t 个石子往上移动一层,那么 B 一定能再次将这 t 个石子再往上移动一层,直到最后到达根节点的那一步一定是 B 操作的。
有了上面的结论,两个玩家的目的就是把奇数层的所有石子往上推 1 层即可,最后如果轮到玩家 A,但奇数层没有石子了,那么 A 输。
这就转化成了经典的 Nim 问题:n 堆石子,每次可以取一堆中任意非零数量的石子,问谁能最后一步取完。(详见百度百科:戳这儿)
括号序列 + splay,维护奇数、偶数层的异或和,支持点插入、点修改操作。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std; 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 100010
#define maxm 200010
#define maxnode 200010
#define LL long long int m, head[maxn], next[maxm], to[maxm];
void AddEdge(int a, int b) {
to[++m] = b; next[m] = head[a]; head[a] = m;
swap(a, b);
to[++m] = b; next[m] = head[a]; head[a] = m;
return ;
}
int clo, dl[maxn], dr[maxn], dep[maxn], inV[maxn], Dep[maxnode], Val[maxnode];
void dfs(int u, int pa) {
dl[u] = ++clo; Dep[clo] = dep[u]; Val[clo] = inV[u];
for(int e = head[u]; e; e = next[e]) if(to[e] != pa) {
dep[to[e]] = dep[u] + 1;
dfs(to[e], u);
}
dr[u] = ++clo;
return ;
} struct Node {
int v, siz, sum[2]; bool dep;
Node() {}
Node(int _, bool __): v(_), dep(__) {}
} ns[maxnode];
int fa[maxnode], ch[maxnode][2], L;
void maintain(int o) {
ns[o].siz = 1; ns[o].sum[0] = ns[o].v * (ns[o].dep ^ 1); ns[o].sum[1] = ns[o].v * ns[o].dep;
for(int i = 0; i < 2; i++) if(ch[o][i]) {
ns[o].siz += ns[ch[o][i]].siz;
for(int j = 0; j < 2; j++) ns[o].sum[j] ^= ns[ch[o][i]].sum[j];
}
return ;
}
void build(int& o, int l, int r) {
if(l > r){ o = 0; return ; }
int mid = l + r >> 1; ns[o = mid] = Node(Val[mid], Dep[mid] & 1);
build(ch[o][0], l, mid - 1); build(ch[o][1], mid + 1, r);
if(ch[o][0]) fa[ch[o][0]] = o;
if(ch[o][1]) fa[ch[o][1]] = o;
return maintain(o);
}
void rotate(int u) {
int y = fa[u], z = fa[y], l = 0, r = 1;
if(z) ch[z][ch[z][1]==y] = u;
if(ch[y][1] == u) swap(l, r);
fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
ch[y][l] = ch[u][r]; ch[u][r] = y;
maintain(y); maintain(u);
return ;
}
void splay(int u) {
while(fa[u]) {
int y = fa[u], z = fa[y];
if(z) {
if(ch[y][0] == u ^ ch[z][0] == y) rotate(u);
else rotate(y);
}
rotate(u);
}
return ;
}
int splitl(int u) {
splay(u);
int tmp = ch[u][0];
fa[tmp] = ch[u][0] = 0;
return maintain(u), tmp;
}
int splitr(int u) {
splay(u);
int tmp = ch[u][1];
fa[tmp] = ch[u][1] = 0;
return maintain(u), tmp;
}
int merge(int a, int b) {
if(!a) return b;
if(!b) return a;
while(ch[a][1]) a = ch[a][1];
splay(a);
ch[a][1] = b; fa[b] = a;
return maintain(a), a;
}
void Split(int ql, int qr, int& lrt, int& mrt, int& rrt) {
lrt = splitl(ql); mrt = qr; rrt = splitr(mrt);
// printf("Split %d %d -> %d %d %d\n", ql, qr, lrt, mrt, rrt);
return ;
}
void Merge(int a, int b, int c) {
a = merge(a, b); merge(a, c);
return ;
}
int query(int u) {
int lrt, mrt, rrt;
Split(dl[u], dr[u], lrt, mrt, rrt);
int ans = ns[mrt].sum[(Dep[dl[u]]&1)^1];
// printf("%d ", ans);
Merge(lrt, mrt, rrt);
return ans;
} #define MOD 50007
int hd[MOD], nxt[maxn], val[maxn], ToT;
void insert(int v) {
int x = v % MOD;
val[++ToT] = v; nxt[ToT] = hd[x]; hd[x] = ToT;
return ;
}
int gid(int v) {
int x = v % MOD;
for(int e = hd[x]; e; e = nxt[e]) if(val[e] == v)
return e;
return 0;
} int main() {
int n = read(); L = read() + 1;
for(int i = 1; i <= n; i++) inV[i] = read() % L, insert(i);
for(int i = 1; i < n; i++) {
int a = read(), b = read();
AddEdge(a, b);
}
dfs(1, 0);
// for(int i = 1; i <= n; i++) printf("%d: [%d, %d]\n", i, dl[i], dr[i]);
int tmp = 0; build(tmp, 1, clo); int q = read(), lst = 0;
while(q--) {
int tp = read();
if(tp == 1) {
int u = gid(read() ^ lst);
if(query(u)) puts("MeiZ"), lst++;
else puts("GTY");
}
if(tp == 2) {
int u = dl[gid(read()^lst)];
splay(u);
ns[u].v = (read() ^ lst) % L;
maintain(u);
}
if(tp == 3) {
int u = gid(read() ^ lst), v = read() ^ lst, x = (read() ^ lst) % L;
insert(v); v = gid(v);
dl[v] = ++clo; dr[v] = ++clo;
Dep[dl[v]] = Dep[dl[u]] + 1;
Val[dl[v]] = x;
int lrt = dl[u], mrt = 0, rrt = splitr(lrt);
build(mrt, dl[v], dr[v]);
mrt = merge(lrt, mrt); merge(mrt, rrt);
}
}
// printf("%d %d\n", clo, ToT); return 0;
}
[BZOJ3729]Gty的游戏的更多相关文章
- BZOJ3729: Gty的游戏(伪ETT)
题面 传送门 前置芝士 巴什博奕 \(Nim\)游戏的改版,我们现在每次最多只能取走\(k\)个石子,那么\(SG\)函数很容易写出来 \[SG(x)=mex_{i=1}^{\min(x,k)}SG( ...
- 【块状树】【博弈论】bzoj3729 Gty的游戏
块状树,每个块的根记录一下当前块内距块根为奇数距离的异或和和偶数距离的异或和,询问的时候讨论一下即可. 总的节点数可能超过50000. #include<cstdio> #include& ...
- 【BZOJ 3729】3729: Gty的游戏 (Splay维护dfs序+博弈)
未经博主同意不得转载 3729: Gty的游戏 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 448 Solved: 150 Description ...
- BZOJ 3729 - Gty的游戏(Staircase 博弈+时间轴分块)
题面传送门 介于自己以前既没有写过 Staircase-Nim 的题解,也没写过时间轴分块的题解,所以现在就来写一篇吧(fog 首先考虑最极端的情况,如果图是一条链,并且链的一个端点是 \(1\),那 ...
- BZOJ 3729: Gty的游戏 [伪ETT 博弈论]【学习笔记】
题意: 给定一棵有根树,每个节点有一些石子,每次可以将不多于k的石子移动到父节点 修改一个点的石子数,插入一个点,询问某棵子树是否先手必胜 显然是一个阶梯Nim 每次最多取k个,找规律或者观察式子易发 ...
- BZOJ 3729 Gty的游戏 ——Splay
很久很久之前,看到Treap,好深啊 很久之前看到Splay,这数据结构太神了. 之后学习了LCT. 然后看到Top-Tree就更觉得神奇了. 知道我见到了这题, 万物基于Splay 显然需要维护子树 ...
- BZOJ 3729 GTY的游戏
伪ETT? 貌似就是Splay维护dfn = = 我们首先观察这个博弈 这个博弈直接%(l+1)应该还是很显然的 因为先手怎么操作后手一定能保证操作总数取到(l+1) 于是就变成阶梯Nim了 因为对于 ...
- [BZOJ3786]星系探索(伪ETT)
3786: 星系探索 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 1638 Solved: 506[Submit][Status][Discuss ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
随机推荐
- 进击的Python【第二十一章】
s14day21 上节内容回顾: 1.请求周期 url> 路由 > 函数或类 > 返回字符串或者模板语言? Form表单提交: 提交 -> url > 函数或类中的方法 ...
- [LeetCode] First Bad Version 第一个坏版本
You are a product manager and currently leading a team to develop a new product. Unfortunately, the ...
- [LeetCode] Valid Parentheses 验证括号
Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the inpu ...
- MVC使用ajax异步刷新时怎样输出从后台中传过来的JSON数据
前言 这几天在学习MVC使用AJAX异步刷,因为是新手.所以在js中传参数到后台以及后台返回数据到前台怎么接受,怎么前台遍历出JSON数据都开始不知道,相信新手在使用时跟我一样会遇到,这里我就和大家分 ...
- Java jdbc访问sqlserver,oracle数据库
1.JDBC访问Oracle数据库 public class Jdbc_Oracle { // 静态代码块,只会执行一次,类似C#静态构造方法 static { try { // 加载数据库驱动一次 ...
- Fiddler抓包工具使用基础
官网下载Fiddler Fiddler的官方网站: www.fiddler2.com Fiddler是最强大最好用的Web调试工具之一,它能记录所有客户端和服务器的http和https请求,允许你监 ...
- Markdown
1. 斜体和粗体 代码: *斜体*或_斜体_ **粗体** ***加粗斜体*** ~~删除线~~ 显示效果: 这是一段斜体 这是一段粗体 这是一段加粗斜体 这是一段删除线 2. 分级标题 第一种写法: ...
- MySQL、Oracle和SQL Server的分页查询语句
假设当前是第PageNo页,每页有PageSize条记录,现在分别用Mysql.Oracle和SQL Server分页查询student表. 1.Mysql的分页查询: SELECT * FROM s ...
- c#上级目录
本文实例讲述了C#的path.GetFullPath 获取上级目录实现方法,分享给大家供大家参考.具体实现方法如下: string path = new directoryinfo("../ ...
- canvas-渐变文字
html要求: <body onload="draw('canvas');"> body这里的onload一定要写,在这个处理模式下,是在body这里执行加载页面完成后 ...