题目

描述

题目大意

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

每次将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. 在vs2010下编译boost

    1. 解压缩后,运行bootstrap.bat批处理文件,得到bjam.exe; 2. 在vs2010下编译boost boost最新版本已经支持vs2010,然而直接下载编译会发现一堆bug: 首先 ...

  2. 开发中运行mysql脚本,发现提示mysql提示Column count doesn't match value count at row 1错误

    开发中运行mysql脚本,发现提示mysql提示Column count doesn't match value count at row 1错误, 调试后发现是由于写的SQL语句里列的数目和后面的值 ...

  3. code rain???

    Everybody loves the visual effect of the falling binary code known as ” Rain ” in The Matrix. In thi ...

  4. 2-JDK环境变量配置和验证

    背景: 官网下载,默认路径安装,如下图,java目录下有两个文件夹:jdk和jre: 1.计算机 -> 右击 -> 属性 -> 选择左侧的'高级系统设置' 2.高级系统设置 -> ...

  5. spring @Transactional注解参数详解(13)

    事物注解方式: @Transactional 当标于类前时, 标示类中所有方法都进行事物处理 , 例子: 1 @Transactional public class TestServiceBean i ...

  6. table 单列宽度设置

    参考:https://blog.csdn.net/lunhui1994_/article/details/81120579 效果: html: <!DOCTYPE html> <ht ...

  7. Spark运行基本流程

  8. 重写(Overriding)和重载(Overloading)

    方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式. (1)方法重载是一个类中定义了多 ...

  9. leetcode-973-最接近原点的K个点

    题目描述: 可参考:题215 方法一:排序 class Solution: def kClosest(self, points: List[List[int]], K: int) -> List ...

  10. 基于V8引擎的C++和JS的相互交互

    基于什么原因略! 1. 脚本引擎的基本功能 V8只是一个JS引擎.去除它的特点功能出处,它必须要实现JS引擎的几个基础功能: 脚本执行:脚本可能是一个表达式:一段js代码:或者一个文件执行表达式返回j ...