[bzoj3729]Gty的游戏
2016-09-12 20:00
465 查看
Description
给定一棵n个节点的数,第i个节点上有ai个石子。有两个人在van游戏,每次操作者可以从x的子树中的任意一个点选择不超过m个石子移动到它的父亲。
给定t次操作,每次操作询问为x的子树中做游戏先手是否必胜,或者修改一个点的石子数量,又或者是给x新增一个儿子y,石子个数为z。
n,t<=50000,保证任何时刻树中结点个数和编号不超过50000
Solution
如果你看到了这里,先别急着往下看。因为出题人有毒!!!
说好的结点个数不超过50000呢?
好了,现在我们来讲讲正解。
首先你得会经典NIM游戏。
我们先来分析一下sg函数值。
sg[x]=mexmin(x,m)i=1(sg[x−i])且sg[0]=0
归纳可得sg[x]=xmod(m+1)
然后,我们可以发现这是个阶梯NIM游戏。
简单点讲,我们只需要关注奇数层的异或和。(根节点的深度为0)
为什么呢?因为你移动偶数层,别人就可以立马把它移到奇数层。
所以移动偶数层等于没动。
然后就是要处理子树问题了。
动态加点可以用LCT,但子树处理似乎要用Euler Tour Tree…..
本蒟蒻不会写。。。
那么我们发现每次加入的点都是新的(似乎直接插入一棵子树也不是问题)
那么我们可以用splay来维护dfs序。
由于序列是动态的,所以我们需要动态维护每个点的size。
不过简单点想,一个节点所掌管的区间就是这个节点为左端点,节点右边第一个深度小于等于它自己的那个点为右端点。
于是我们可以维护区间min_dep值。
然后维护深度为奇数和偶数的层数的异或和。(奇数和总和我不拦着你)
注意这道题要在右边插入一个虚拟节点,但不用在左边插。
还有,插入没必要搞得那么麻烦,只插入一个点的话,直接插就好啦。
从a_crazy_czy那里学来的姿势,这道题可以用定期重构来做。
似乎还可以用树分块(Gty系列)。
Code
#include<cstdio> #include<cstring> #include<algorithm> #define fo(i,a,b) for(int i=a;i<=b;i++) #define rep(i,a) for(int i=last[a];i;i=next[i]) #define N 100005 #define Null N-1 using namespace std; const int inf=0x7fffffff; int t [2],f ,size ,sg ,sg_0 ,sg_1 ,dep ; int d ,n,m,q,x,y,z,l,tot,cnt,ans,bz,root; int last ,to[N*2],next[N*2]; void add(int x,int y) { to[++l]=y;next[l]=last[x];last[x]=l; } void updata(int x) { int l=t[x][0],r=t[x][1]; dep[x]=min(d[x],min(dep[l],dep[r])); size[x]=size[l]+size[r]+1; sg_0[x]=sg_0[l]^sg_0[r]; sg_1[x]=sg_1[l]^sg_1[r]; if (d[x]&1) sg_1[x]^=sg[x]; else sg_0[x]^=sg[x]; } int son(int x) {return t[f[x]][1]==x;} void rotate(int x) { int y=f[x],z=son(x);f[x]=f[y]; if (f[x]) t[f[x]][son(y)]=x; if (t[x][1-z]) f[t[x][1-z]]=y; f[y]=x;t[y][z]=t[x][1-z];t[x][1-z]=y; updata(y);updata(x); } void splay(int x,int y) { while (f[x]!=y) { if (f[f[x]]!=y) if (son(x)==son(f[x])) rotate(f[x]); else rotate(x); rotate(x); } if (!y) root=x; } void dfs(int x,int y) { if (y) d[x]=d[y]+1; if (root) f[x]=root,t[root][1]=x; splay(x,0); rep(i,x) if (to[i]!=y) dfs(to[i],x); } int get(int x,int y) { if (dep[t[x][0]]<=y) return get(t[x][0],y); else if (d[x]<=y) return x; else return get(t[x][1],y); } int main() { scanf("%d%d",&n,&m);d[0]=dep[0]=inf; fo(i,1,n) scanf("%d",&sg[i]),sg[i]%=(m+1); fo(i,1,n-1) scanf("%d%d",&x,&y),add(x,y),add(y,x); d[1]=1;dfs(1,0);f[Null]=root;t[root][1]=Null;splay(Null,0); for(scanf("%d",&q);q;q--) { scanf("%d",&bz); if (bz==1) { scanf("%d",&x);x^=cnt; splay(x,0);int y=get(t[x][1],d[x]); splay(y,x); if (d[x]&1) ans=sg_0[t[y][0]]; else ans=sg_1[t[y][0]]; if (ans) printf("MeiZ\n"),cnt++; else printf("GTY\n"); } else if (bz==2) { scanf("%d%d",&x,&y);x^=cnt;y^=cnt; splay(x,0);sg[x]=y%(m+1);updata(x); } else { scanf("%d%d%d",&x,&y,&z);x^=cnt;y^=cnt;z^=cnt; d[y]=d[x]+1;sg[y]=z%(m+1); splay(x,0);f[t[x][1]]=y;f[y]=x; t[y][1]=t[x][1];t[x][1]=y; updata(t[y][1]);updata(y);updata(x); } } }
相关文章推荐
- 组原 之 数字
- JavaScript学习笔记之DOM对象操作html元素
- POJ 2065 SETI(高斯消元解同余方程组)
- LeetCode 383:Ransom Note
- 并查集
- 160809132 梁佳佳
- Django踩坑
- Android Volley使用之二:Volley请求网络图片
- sass实战演练06 - 把布局拆分为12列(2):拆分和偏移
- 行内元素和块元素
- MongoDB 的用户认证
- 【JZOJ4771】爬山
- 第9章:面向对象变量与关键字
- Mac 下gdb 的安装过程
- opencv实现多图像读取并显示,sprintf_s函数,static_cast
- stream_context_create() 模拟 POST、GET数据
- HDU 5873 - Football Match【2016大连区域赛网络赛1006】
- Hive在spark2.0.0启动时无法访问../lib/spark-assembly-*.jar: 没有那个文件或目录的解决办法
- javascript网页特效——table
- java util :获取国家省份城市工具类