[BZOJ]3729: Gty的游戏 splay+博弈

Description

某一天gty在与他的妹子玩游戏。
妹子提出一个游戏,给定一棵有根树,每个节点有一些石子,每次可以将不多于L的石子移动到父节点,询问将某个节点的子树中的石子移动到这个节点先手是否有必胜策略。
gty很快计算出了策略。
但gty的妹子十分机智,她决定修改某个节点的石子或加入某个新节点。
gty不忍心打击妹子,所以他将这个问题交给了你。
另外由于gty十分绅士,所以他将先手让给了妹子。

Solution

这题一看就先推一推SG函数,然后就维护一下这棵树,用 d f s dfs dfs序看待的话就变成一个维护序列问题,支持查询区间异或和还有插入修改。
还有一个问题就是怎么获得一棵子树的区间,直接每个点按照入栈出栈序插两个点就行了。

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=60010;
const int inf=2147483647;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
map<int,int>id;int ID=0;
int n,L,pos[Maxn][2],ll[Maxn],stone[Maxn],dep[Maxn],tot=0;
int sg(int d,int x)
{
	if(!(d&1))return 0;
	return x%(L+1);
}
vector<int>h;
struct Edge{int y,next;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y)
{
	int t=++len;
	e[t].y=y;e[t].next=last[x];last[x]=t;
}
void dfs(int x,int ff)
{
	h.push_back(x);dep[x]=dep[ff]+1;
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==ff)continue;
		dfs(y,x);
	}
	h.push_back(x);
}
int fa[Maxn<<1],son[Maxn<<1][2],v[Maxn<<1][2],sum[Maxn<<1][2],sz[Maxn<<1],root;
void up(int x)
{
	int lc=son[x][0],rc=son[x][1];
	sum[x][0]=v[x][0],sum[x][1]=v[x][1],sz[x]=1;
	if(lc)sum[x][0]^=sum[lc][0],sum[x][1]^=sum[lc][1],sz[x]+=sz[lc];
	if(rc)sum[x][0]^=sum[rc][0],sum[x][1]^=sum[rc][1],sz[x]+=sz[rc];
}
int build(int l,int r)
{
	if(l>r)return 0;
	if(l==r)return l;
	int mid=l+r>>1;
	son[mid][0]=build(l,mid-1);if(son[mid][0])fa[son[mid][0]]=mid;
	son[mid][1]=build(mid+1,r);if(son[mid][1])fa[son[mid][1]]=mid;
	up(mid);
	return mid;
}
void rotate(int x)
{
	int y=fa[x],z=fa[y],w=(son[y][0]==x);
	son[y][w^1]=son[x][w];if(son[x][w])fa[son[x][w]]=y;
	son[z][son[z][1]==y]=x;fa[x]=z;
	son[x][w]=y;fa[y]=x;
	up(y);up(x);
}
void splay(int x,int rt)
{
	while(fa[x]!=rt)
	{
		int y=fa[x],z=fa[y];
		if(z==rt)rotate(x);
		else rotate(((son[z][1]==y)==(son[y][1]==x))?y:x),rotate(x);
	}
	if(!rt)root=x;
}
void split(int l,int r){splay(l,0),splay(r,l);}
int find_by_rank(int rk)
{
	int x=root;
	while(rk!=sz[son[x][0]]+1)
	{
		int lc=son[x][0],rc=son[x][1];
		if(rk<=sz[lc])x=lc;
		else rk-=(sz[lc]+1),x=rc;
	}
	return x;
}
void insert(int ff,int x,int V)
{
	stone[x]=V;dep[x]=dep[ff]+1;
	sz[++tot]=1;pos[x][0]=tot;
	v[tot][0]=sum[tot][0]=sg(dep[x],stone[x]);
	v[tot][1]=sum[tot][1]=sg(dep[x]+1,stone[x]);
	sz[++tot]=1;pos[x][1]=tot;
	v[tot][0]=sum[tot][0]=v[tot][1]=sum[tot][1]=0;
	int l,r;
	splay(pos[ff][1],0);
	l=find_by_rank(sz[son[pos[ff][1]][0]]),r=pos[ff][1];
	split(l,r);
	son[r][0]=tot-1;fa[tot-1]=r;
	son[tot-1][1]=tot;fa[tot]=tot-1;
	up(tot-1),up(r),up(l);
}
int main()
{
	n=read(),L=read();
	for(int i=1;i<=n;i++)stone[i]=read(),id[i]=++ID;
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		ins(x,y),ins(y,x);
	}
	dep[0]=-1;dfs(1,0);
	pos[0][0]=++tot;
	sum[tot][0]=sum[tot][1]=v[tot][0]=v[tot][1]=0;
	sz[tot]=1;
	for(int i=0;i<h.size();i++)
	{
		int x=h[i];
		pos[x][ll[x]++]=++tot;
		if(ll[x]==1)
		{
			sum[tot][0]=v[tot][0]=sg(dep[x],stone[x]);
			sum[tot][1]=v[tot][1]=sg(dep[x]+1,stone[x]);
			sz[tot]=1;
		}
		else
		{
			sum[tot][0]=sum[tot][1]=v[tot][0]=v[tot][1]=0;
			sz[tot]=1;
		}
	}
	pos[0][1]=++tot;
	sum[tot][0]=sum[tot][1]=v[tot][0]=v[tot][1]=0;
	sz[tot]=1;
	root=build(1,tot);
	int m=read(),cnt=0;
	while(m--)
	{
		int op=read(),x=id[read()^cnt],y,z;
		if(op==1)
		{
			int l,r;
			splay(pos[x][0],0);
			l=find_by_rank(sz[son[pos[x][0]][0]]);
			splay(pos[x][1],0);r=find_by_rank(sz[son[pos[x][1]][0]]+2);
			split(l,r);
			if(!(dep[x]&1))
			{
				if(!(sum[son[r][0]][0]^v[pos[x][0]][0]))puts("GTY");
				else puts("MeiZ"),cnt++;
			}
			else
			{
				if(!(sum[son[r][0]][1]^v[pos[x][0]][1]))puts("GTY");
				else puts("MeiZ"),cnt++;
			}
		}
		else if(op==2)
		{
			y=(read()^cnt);stone[x]=y;
			splay(pos[x][0],0);
			v[pos[x][0]][0]=sg(dep[x],stone[x]);
			v[pos[x][0]][1]=sg(dep[x]+1,stone[x]);
			up(x);
		}
		else
		{
			id[read()^cnt]=++ID,z=(read()^cnt);
			insert(x,ID,z);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值