zoukankan      html  css  js  c++  java
  • [JZOJ4759] 【雅礼联考GDOI2017模拟9.4】石子游戏

    题目

    描述

    在这里插入图片描述

    题目大意

    在一棵树上,每个节点都有些石子。
    每次将mm颗石子往上移,移到根节点就不能移了。
    双方轮流操作,问先手声还是后手胜。
    有三种操作:
    1、 询问以某个节点为根的答案。
    2、 改变某个点的石子数。
    3、 在树中加入一个点。


    思考历程

    这是一道博弈题。
    意味着我连暴力都不会打。
    所以放弃治疗。


    正解

    首先,偶数层的石子是没有意义的。
    如果移动了偶数层的石子,另一方就可以模仿你的操作,继续移动这颗石子。
    所以我们只需要考虑奇数层的石子,每次移动11mm颗石子,移动到上一层之后消失。
    这就转化成了一个Nimk问题。
    按照套路,将每个石子数模m+1m+1(个人感性理解:当你移动11mm颗石子的时候,别人可以移动石子使得你们移的总数为m+1m+1
    然后异或起来,如果为00就必败,反之必胜。

    然后这题就差不多做完了。
    我们只需要维护子树中偶数层的异或和就可以了。
    LCT?ETT?
    都行。
    在维护的时候可以分别建立两棵树,以11号点为准,偶数层和奇数层分别建一棵树。
    对于某个节点,如果它在奇数层,那么奇数层的树中它有值,偶数层的树中值为00
    反之亦然。
    这样处理起来就比较方便了。

    话说这题真的有毒,说好保证编号不超过5000050000,结果真香了。
    害得我调试了至少一天半……
    开到6000060000就能过了。


    代码

    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
    "),lastans++;
    			else
    				printf("No
    ");
    		}
    		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;
    }
    

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


    总结

    像这样的博弈类问题普遍有一个套路。
    分为奇数层和偶数层,就可以不管偶数层。
    然后直接处理奇数层即可。

  • 相关阅读:
    C++primer习题3.13
    Indesign技巧
    《转载》虚函数在对象中的内存布局
    C++new失败后如何处理
    sizeof的用法
    转载 C++中虚继承防止二义性
    字符串反转
    回文写法
    C++术语
    QT+VS2008
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145231.html
Copyright © 2011-2022 走看看