描述
在一棵树上,每个节点都有些石子。
每次将 m m m颗石子往上移,移到根节点就不能移了。
双方轮流操作,问先手声还是后手胜。
有三种操作:
1、 询问以某个节点为根的答案。
2、 改变某个点的石子数。
3、 在树中加入一个点。
这是一道博弈题。
意味着我连暴力都不会打。
所以放弃治疗。
首先,偶数层的石子是没有意义的。
如果移动了偶数层的石子,另一方就可以模仿你的操作,继续移动这颗石子。
所以我们只需要考虑奇数层的石子,每次移动 1 1 1到 m m m颗石子,移动到上一层之后消失。
这就转化成了一个Nimk问题。
按照套路,将每个石子数模 m + 1 m+1 m+1(个人感性理解:当你移动 1 1 1到 m m m颗石子的时候,别人可以移动石子使得你们移的总数为 m + 1 m+1 m+1)
然后异或起来,如果为 0 0 0就必败,反之必胜。
然后这题就差不多做完了。
我们只需要维护子树中偶数层的异或和就可以了。
LCT?ETT?
都行。
在维护的时候可以分别建立两棵树,以 1 1 1号点为准,偶数层和奇数层分别建一棵树。
对于某个节点,如果它在奇数层,那么奇数层的树中它有值,偶数层的树中值为 0 0 0。
反之亦然。
这样处理起来就比较方便了。
话说这题真的有毒,说好保证编号不超过 50000 50000 50000,结果真香了。
害得我调试了至少一天半……
开到 60000 60000 60000就能过了。
using namespace std;
#include
#include
#include
#include
#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;
}
整篇似乎也没什么需要注释的……
像这样的博弈类问题普遍有一个套路。
分为奇数层和偶数层,就可以不管偶数层。
然后直接处理奇数层即可。