题目

描述

题目大意

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

每次将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. MySQL基础管理

    1.用户管理 1.用户的作用: 登录:管理相对应的库表 2.定义 定义用户名和白名单 all@'10.0.0.%' 命名用户名时,最好不要太长,要和业务相关 白名单类型: user@'10.0.0.5 ...

  2. C++数据类型之实型(浮点型)&科学计数法

    实型(浮点型) **作用**:用于==表示小数== 浮点型变量分为两种: 1. 单精度float 2. 双精度double 两者的**区别**在于表示的有效数字范围不同. float类型数据,需在数据 ...

  3. 同步图计算实现最短路径Dijkstra算法

    同上篇讲述pageRank一样,考虑一个顶点V. 根据顶点算法通常步骤1) 接收上个超步发出的入邻居的消息2) 计算当前顶点的值3) 向出邻居发消息 1.接收入邻居的消息 2.求入邻居的最小值,加上顶 ...

  4. gmock 简单笔记

    std::shared_ptr<MockThreadRCInvester> spMockaAcc; HelperThreadRCInvester helperAcc; // spMockA ...

  5. Dubbo的底层实现原理和机制

    –高性能和透明化的RPC远程服务调用方案 –SOA服务治理方案 Dubbo缺省协议采用单一长连接和NIO异步通讯, 适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况

  6. BCZM : 1.5

    https://blog.csdn.net/zs634134578/article/details/18046317 有很多服务器存储数据,假设一个机器仅存储一个标号为ID的记录,假设机器总量在10亿 ...

  7. Windows taskkill

    TASKKILL [/S system [/U username [/P [password]]]]         { [/FI filter] [/PID processid | /IM imag ...

  8. python中循环引用导致内存泄漏小案例

    首先定义一个Person类和一个Dog类,然后分别实例化对象p和d,给p对象添加一个pet属性 给d对象添加一个master属性此时Person和Dog的应用计数都为2,当del p 和del d后P ...

  9. SSM14-通过AOP实现日志记录

    1.要求使用AOP思想,实现对每一个用户登陆后,将以下信息保存在数据库 1>登陆时间 2>退出时间 3>登录的IP地址 4>访问点URL(访问了那些Controller) 5& ...

  10. Linux课程---14、linux下lamp环境如何安装

    Linux课程---14.linux下lamp环境如何安装 一.总结 一句话总结: 要按顺序安装,比如apache需要在php之前安装, 一.安装 gcc 编译器 二.卸载 rpm 安装的 http ...