[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"的个数。

输入示例

2 1000
0 0
1 2
1
1 1

输出示例

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;
}
时间: 2024-12-11 18:18:16

[BZOJ3729]Gty的游戏的相关文章

【块状树】【博弈论】bzoj3729 Gty的游戏

块状树,每个块的根记录一下当前块内距块根为奇数距离的异或和和偶数距离的异或和,询问的时候讨论一下即可. 总的节点数可能超过50000. #include<cstdio> #include<cmath> using namespace std; #define N 100001 int n,m,L,a[N]; int en,v[N<<1],next[N<<1],first[N]; int e2,v2[N<<1],nex2[N<<1],f

博弈论题表(好少~~~)

bzoj2017:[Usaco2009 Nov]硬币游戏 *用了一小点思想的傻逼dp(记忆化搜索)bzoj1188:[HNOI2007]分裂游戏 **很神奇的把游戏拆分为子游戏的方法bzoj1022:[SHOI2008]小约翰的游戏John *傻逼SJ定理题bzoj1982:[Spoj 2021]Moving Pebbles **思路不一般的并不是很难证的傻逼结论题bzoj2688:Green Hackenbush **切树模型的傻逼概率dp(外有特殊的飞行技巧-判0剪枝)bzoj2281:[S

[BZOJ3786]星系探索(伪ETT)

3786: 星系探索 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1638  Solved: 506[Submit][Status][Discuss] Description 物理学家小C的研究正遇到某个瓶颈. 他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球.主星球没有依赖星球. 我们定义依赖关系如下:若星球a的依赖星球是b,则有星球a依赖星球b.此外,依赖关

20170913自制猜数字游戏

/* 猜数字:系统随机生成一个四位数,请根据下列判断猜出来 A:数值正确,位置正确 B:数值正确,位置不正确 C:数值不正确 */ #include<stdio.h> #include<time.h> #include<stdlib.h> #pragma warning (disable:4996) #define pUCharHead unsigned char * //以数组形式返回n个无重复的随机数,范围可指定[min,max] pUCharHead GenNoR

洛谷P1199 三国游戏

题目描述 小涵很喜欢电脑游戏,这些天他正在玩一个叫做<三国>的游戏. 在游戏中,小涵和计算机各执一方,组建各自的军队进行对战.游戏中共有 N 位武将(N为偶数且不小于 4),任意两个武将之间有一个"默契值",表示若此两位武将作为一对组合作战时,该组合的威力有多大.游戏开始前,所有武将都是自由的(称为自由武将,一旦某个自由武将被选中作为某方军队的一员,那么他就不再是自由武将了),换句话说,所谓的自由武将不属于任何一方. 游戏开始,小涵和计算机要从自由武将中挑选武将组成自己的军

游戏开发 系统app开发游戏定制开发找谁

梦幻珍珠港理财拆分游戏系统软件开发怎么做?找[江生:185-2911-8412 微电]. 梦幻珍珠港拆分游戏平台开发.梦幻珍珠港理财全网模式开发.梦幻珍珠港收益模式介绍开发. 梦幻珍珠港拆分游戏系统源码搭建平台!!我们是软件开发,玩家勿扰!非平台客服,玩家勿扰!] 游戏规则: 一.开发新会员要从您的库房扣除3 03颗珍珠,体系扣除5颗珍珠,新会员有298颗珍珠. 二.推荐老友,推荐人能够得到第一代会员的2%,第二代会员的1%,第三代会员的0.5%,一代的收益就是5.96颗珍珠奖赏(可转换为等量可

游戏服务器开发需要学习的技术

一,游戏服务器编程语言的选择 所谓的游戏服务器编程语言其实有很多,基本上任何一种语言都可以作为游戏服务器的编程语言.这需要根据自己游戏的类型和要求加以选择.比如C++,Java ,Erlang,go等等.目前我用过的只有C++和Java.但是以Java为主.所以接下来就以自己的经验,谈谈以Java为核心的游戏服务器开发技术体系. Java目前作为游戏服务器开发语言已经很是普遍.但是大多数是作为页游或手游的服务器,而端游戏一般选择C++,因为端游对服务器的性能要求相对比较高一些.两种语言各有利弊.

赛码网算法: 格子游戏

格子游戏 题目描述 有n个格子,从左到右放成一排,编号为1-n.共有m次操作,有3种操作类型:1.修改一个格子的权值,2.求连续一段格子权值和,3.求连续一段格子的最大值.对于每个2.3操作输出你所求出的结果. 输入输入第一行两个整数,n表示格子个数,m表示操作次数,n和m中间用空格隔开:接下来输入n行,每行一个整数表示一个格子的权值接下来输入m行,每行有三个整数,中间用空格隔开:第一个是选择的操作类型1-3,第二和第三个整数是操作格子的编号.样例输入3 37892 1 33 1 32 1 2输

【BZOJ4945】[Noi2017]游戏 2-SAT

[BZOJ4945][Noi2017]游戏 题目描述 题解:2-SAT学艺不精啊! 这题一打眼看上去是个3-SAT?哎?3-SAT不是NPC吗?哎?这题x怎么只有8个?暴力走起! 因为x要么不是A要么不是B,所以直接2^8枚举所有x就行了.然后就变成了一个2-SAT问题.假设有两场游戏1,2,分别可以使用的地图为A1,A2,B1,B2,如果有一个限制是1 A 2 A,那么选A1就必须选A2,然后我这个沙茶就开开心心的拿了55分. 为什么不对?我建出来的图显然不对偶啊!考虑逆否命题,选A1就必须选