题目

描述

题目大意

在一棵树上,每个节点都有些石子。

每次将mmm颗石子往上移,移到根节点就不能移了。

双方轮流操作,问先手声还是后手胜。

有三种操作:

1、 询问以某个节点为根的答案。

2、 改变某个点的石子数。

3、 在树中加入一个点。


思考历程

这是一道博弈题。

意味着我连暴力都不会打。

所以放弃治疗。


正解

首先,偶数层的石子是没有意义的。

如果移动了偶数层的石子,另一方就可以模仿你的操作,继续移动这颗石子。

所以我们只需要考虑奇数层的石子,每次移动111到mmm颗石子,移动到上一层之后消失。

这就转化成了一个Nimk问题。

按照套路,将每个石子数模m+1m+1m+1(个人感性理解:当你移动111到mmm颗石子的时候,别人可以移动石子使得你们移的总数为m+1m+1m+1)

然后异或起来,如果为000就必败,反之必胜。

然后这题就差不多做完了。

我们只需要维护子树中偶数层的异或和就可以了。

LCT?ETT?

都行。

在维护的时候可以分别建立两棵树,以111号点为准,偶数层和奇数层分别建一棵树。

对于某个节点,如果它在奇数层,那么奇数层的树中它有值,偶数层的树中值为000。

反之亦然。

这样处理起来就比较方便了。

话说这题真的有毒,说好保证编号不超过500005000050000,结果真香了。

害得我调试了至少一天半……

开到600006000060000就能过了。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <cassert>
#include <algorithm>
#define N 60010
struct Node *null;
struct Node{
Node *fa,*c[2];
bool is_root,rev;
int sum,su;
inline bool getson(){return fa->c[0]!=this;}
inline void update(){
sum=c[0]->sum^c[1]->sum^su;
}
inline void reserve(){
swap(c[0],c[1]);
rev^=1;
}
inline void pushdown(){
if (rev){
c[0]->reserve();
c[1]->reserve();
rev=0;
}
}
inline void push(){
if (!is_root)
fa->push();
pushdown();
}
inline void rotate(){
Node *y=fa,*z=y->fa;
if (y->is_root){
y->is_root=0;
is_root=1;
}
else
z->c[y->getson()]=this;
bool k=getson();
fa=z;
y->c[k]=c[k^1];
c[k^1]->fa=y;
c[k^1]=y;
y->fa=this;
sum=y->sum;
y->update();
}
inline void splay(){
push();
while (!is_root){
if (!fa->is_root){
if (getson()!=fa->getson())
rotate();
else
fa->rotate();
}
rotate();
}
}
inline Node *access(){
Node *x=this,*y=null;
for (;x!=null;y=x,x=x->fa){
x->splay();
x->su^=y->sum^x->c[1]->sum;
x->c[1]->is_root=1;
x->c[1]=y;
y->is_root=0;
x->update();
}
return y;
}
inline void mroot(){
access()->reserve();
}
inline void link(Node *y){
access();
splay();
y->mroot();
y->splay();
y->fa=this;
su^=y->sum;
update();
}
} d[2][N];
int n,m;
int a[N];
struct EDGE{
int to;
EDGE *las;
} e[N*2];
int ne;
EDGE *last[N];
bool col[N];
void build(int x,int fa){
d[col[x]][x]={null,null,null,1,0,0,a[x]};
d[col[x]^1][x]={null,null,null,1,0,0,0};
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa){
col[ei->to]=col[x]^1;
build(ei->to,x);
d[0][x].link(&d[0][ei->to]);
d[1][x].link(&d[1][ei->to]);
}
}
int main(){
null=new Node;
*null={null,null,null,0,0,0,0};
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]),a[i]%=m+1;
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
last[u]=&(e[++ne]={v,last[u]});
last[v]=&(e[++ne]={u,last[v]});
}
build(1,0);
d[0][1].mroot(),d[1][1].mroot();
int T,lastans=0;
scanf("%d",&T);
while (T--){
int op;
scanf("%d",&op);
if (op==1){
int x;
scanf("%d",&x),x^=lastans;
Node *p=&d[col[x]^1][x];
p->access();
p->splay();
if (p->sum^p->c[0]->sum)
printf("Yes\n"),lastans++;
else
printf("No\n");
}
else if (op==2){
int x,y;
scanf("%d%d",&x,&y),x^=lastans,y^=lastans;
y%=m+1;
Node *p=&d[col[x]][x];
p->access();
p->splay();
p->su^=a[x]^y;
a[x]=y;
p->update();
}
else{
int u,v,x;
scanf("%d%d%d",&u,&v,&x),u^=lastans,v^=lastans,x^=lastans;
x%=m+1;
a[v]=x;
col[v]=col[u]^1;
d[col[v]][v]={null,null,null,1,0,0,x};
d[col[v]^1][v]={null,null,null,1,0,0,0};
d[0][u].link(&d[0][v]);
d[1][u].link(&d[1][v]);
}
}
return 0;
}

整篇似乎也没什么需要注释的……


总结

像这样的博弈类问题普遍有一个套路。

分为奇数层和偶数层,就可以不管偶数层。

然后直接处理奇数层即可。

[JZOJ4759] 【雅礼联考GDOI2017模拟9.4】石子游戏的更多相关文章

  1. 【NOIP2016提高A组模拟8.17】(雅礼联考day1)总结

    考的还ok,暴力分很多,但有点意外的错误. 第一题找规律的题目,推了好久.100分 第二题dp,没想到. 第三题树状数组.比赛上打了个分段,准备拿60分,因为时间不够,没有对拍,其中有分段的20分莫名 ...

  2. 【NOIP2016提高A组模拟8.19】(雅礼联考day2)总结

    第一题又有gcd,又有xor,本来想直接弃疗,不过后来想到了个水法: 当两个相邻的数满足条件时,那么他们的倍数也可能满足条件.然后没打,只打了个暴力. 正解就是各种结论,各种定理搞搞. 第二题,想都不 ...

  3. 【NOIP2016提高A组模拟8.19】(雅礼联考day2)公约数

    题目 给定一个正整数,在[1,n]的范围内,求出有多少个无序数对(a,b)满足gcd(a,b)=a xor b. 分析 显然a=b是一定不满足, 我们设\(a>b\), 易得gcd(a,b)&l ...

  4. 【NOIP2016提高A组模拟8.19】(雅礼联考day2)树上路径

    题目 给出一棵树,求出最小的k,使得,且在树中存在路径p,使得k>=S且k<=E.(k为路径p上的边的权值和). 分析 点分治,设当前为x的,求在以x为根的子树中,经过x的路径(包括起点或 ...

  5. 【NOIP2016提高A组模拟8.17】(雅礼联考day1)Binary

    题目 分析 首先每个数对\(2^i\)取模.也就是把每个数的第i位以后删去. 把它们放进树状数组里面. 那么当查询操作, 答案就位于区间\([2^i-x,2^{i-1}-1-x]\)中,直接查询就可以 ...

  6. 【NOIP2016提高A组模拟8.17】(雅礼联考day1)Value

    题目 分析 易证,最优的答案一定是按\(w_i\)从小到大放. 我们考虑dp, 先将w从小到大排个序,再设\(f_{i,j}\)表示当前做到第i个物品,已选择了j个物品的最大值.转移就是\[f_{i, ...

  7. 【NOIP2016提高A组模拟8.17】(雅礼联考day1)Matrix

    题目 分析 假设,我们从\(F_{i,2}\)出发,那么对\(F_{n,n}\)的贡献就是\(某个系数乘以a^{n-i}b^{n-1}r_i\): 同理,如果从\(F_{2,i}\)出发,那么对\(F ...

  8. P7514-[省选联考2021A/B卷]卡牌游戏【贪心】

    正题 题目链接:https://www.luogu.com.cn/problem/P7514 题目大意 给出\(n\)个卡牌有\(a_i/b_i\),开始都是\(a_i\)朝上,将不超过\(m\)张卡 ...

  9. 【GDOI2017 day1】取石子游戏 线段树+区间合并

    题面 如果给你一棵有根树,树根为 1,并且树的每个结点上有一个权值,现在我想知道每个点,除它所在子树以外的结点权值集合的 mex,怎么做呢? 在这里,mex 是定义在集合上的函数,mex(S) 表示 ...

随机推荐

  1. linux 重定向命令

    标准输入,输出和错误 --------------------------------- 文件文件                描写叙述符 ----------------------------- ...

  2. 简单HOOK SSDT实现文件防删除

    http://www.rosoo.net/a/201001/8347.html

  3. PAT_A1043#Is It a Binary Search Tree

    Source: PAT A1043 Is It a Binary Search Tree (25 分) Description: A Binary Search Tree (BST) is recur ...

  4. 剑指offer——29顺时针打印矩阵

    题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数 ...

  5. 史上最全Redis面试题及答案。

    花了大量时间整理了这套Redis面试题 首发50题,绝无仅有,从入门到精通 从基础,高级知识点,再到集群,运维,方案- 弄明白了这些题可以说可以成为面霸了 面试官都得折服,Redis学得怎么样,都来检 ...

  6. [BOI2003]团伙

    题目描述 1920年的芝加哥,出现了一群强盗.如果两个强盗遇上了,那么他们要么是朋友,要么是敌人.而且有一点是肯定的,就是: 我朋友的朋友是我的朋友: 我敌人的敌人也是我的朋友. 两个强盗是同一团伙的 ...

  7. 21-5-split

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

  8. Spark RDD API(scala)

    1.RDD RDD(Resilient Distributed Dataset弹性分布式数据集)是Spark中抽象的数据结构类型,任何数据在Spark中都被表示为RDD.从编程的角度来看,RDD可以简 ...

  9. JavaScript 数组函数 map()

    JavaScript 数组函数 map() 学习心得 map()函数是一个数组函数: 它对数组每个原素进行操作,不对空数组进行操作: 不改变原本的数组,返回新数组: arr.map(function( ...

  10. C# async await 举个栗子

    首先,async 和 await 代表异步执行和等待. async是一个标记,告诉编译器,我可能是一个异步方法. await 代表等待,告诉编译器,这里等我返回结果. 下面,我们简单说一下. 一 , ...