【bzoj2819】Nim

2819: Nim

Time Limit: 20 Sec   Memory Limit: 128 MB
Submit: 3107   Solved: 1173
[ Submit][ Status][ Discuss]

Description

著名游戏设计师vfleaking,最近迷上了Nim。普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取。谁不能取谁输。这个游戏是有必胜策略的。于是vfleaking决定写一个玩Nim游戏的平台来坑玩家。
为了设计漂亮一点的初始局面,vfleaking用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1,2,3,4,...n,在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达。然后他不停地进行如下操作:

1.随机选两个堆v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略,如果有,vfleaking将会考虑将这些石子堆作为初始局面之一,用来坑玩家。
2.把堆v中的石子数变为k。

由于vfleaking太懒了,他懒得自己动手了。请写个程序帮帮他吧。

Input

 第一行一个数n,表示有多少堆石子。
接下来的一行,第i个数表示第i堆里有多少石子。
接下来n-1行,每行两个数v,u,代表v,u间有一条边直接相连。
接下来一个数q,代表操作的个数。
接下来q行,每行开始有一个字符:
如果是Q,那么后面有两个数v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略。
如果是C,那么后面有两个数v,k,代表把堆v中的石子数变为k。

对于100%的数据:
1≤N≤500000, 1≤Q≤500000, 0≤任何时候每堆石子的个数≤32767
其中有30%的数据:
石子堆组成了一条链,这3个点会导致你DFS时爆栈(也许你不用DFS?)。其它的数据DFS目测不会爆。

注意:石子数的范围是0到INT_MAX

Output

对于每个Q,输出一行Yes或No,代表对询问的回答。

Sample Input

【样例输入】
5
1 3 5 2 5
1 5
3 5
2 5
1 4
6
Q 1 2
Q 3 5
C 3 7
Q 1 2
Q 2 4
Q 5 3

Sample Output

Yes
No
Yes
Yes
Yes

HINT

Source

[ Submit][ Status][ Discuss]



真的卡dfs呀。。?

树剖+Nim

Nim游戏:异或和为0的时候无必胜策略,反之则有

代码:
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;

typedef long long LL;

const int maxn = 500100;

queue<int> Q;
int stk[maxn],to;
vector<int> e[maxn];
int n,m,q,a[maxn],w[maxn],cur[maxn];
int top[maxn],fa[maxn],dep[maxn],siz[maxn],son[maxn],id[maxn],ch[maxn][2];
int sum[4 * maxn],tot;
bool flip[maxn],b[maxn];

inline void maintain(int o)
{
	sum[o] = sum[o * 2] ^ sum[o * 2 + 1];
}

inline void build(int o,int l,int r)
{
	if (l == r)
	{
		sum[o] = w[l];
		return;
	}
	int mid = l + r >> 1,lc = o * 2,rc = o * 2 + 1;
	build(lc,l,mid);
	build(rc,mid + 1,r);
	maintain(o);
}

inline void modify(int o,int l,int r,int pos,int x)
{
	if (l == r){sum[o] = x; return;}
	int mid = l + r >> 1,lc = o * 2,rc = o * 2 + 1;
	if (pos <= mid) modify(lc,l,mid,pos,x);
	else modify(rc,mid + 1,r,pos,x);
	maintain(o);
}

inline int query(int o,int l,int r,int al,int ar)
{
	if (al <= l && r <= ar) return sum[o];
	int mid = l + r >> 1,lc = o * 2,rc = o * 2 + 1;
	if (ar <= mid) return query(lc,l,mid,al,ar);
	if (mid < al) return query(rc,mid + 1,r,al,ar);
	if (al <= mid && mid < ar) return query(lc,l,mid,al,mid) ^ query(rc,mid + 1,r,mid + 1,ar); 
}

inline void bfs_init()
{
	int u = 1;
	while (u)
	{
		bool flag = 0;
		if (!b[u])
		{
			dep[u] = dep[fa[u]] + 1;
			b[u] = 1;
		}
		for (int &i = cur[u]; i < e[u].size(); i++)
		{
			int v = e[u][i];
			if (v == fa[u]) continue;
			stk[++to] = fa[v] = u;
			cur[u]++;
			u = v;
			flag = 1;
			break;
		}
		if (flag) continue;
		int tmp = 0;
		for (int i = 0; i < e[u].size(); i++)
		{
			int v = e[u][i];
			if (v == fa[u]) continue;
			siz[u] += siz[v];
			if (siz[v] > siz[son[u]])
			{
				son[u] = v;
				tmp = i;
			}
		}
		swap(e[u][0],e[u][tmp]);
		siz[u]++;
		u = stk[to--];
	}
}

inline void bfs_cut()
{
	int u = 1;
	while (u)
	{
		bool flag = 0;
		if (son[u])
		{
			for (int &i = cur[u]; i < e[u].size(); i++)
			{
				int v = e[u][i];
				if (v == fa[u]) continue;
				top[v] = v == son[u] ? top[u] : v;
				w[id[v] = ++tot] = a[v];
				
				stk[++to] = u;
				cur[u]++;
				u = v;
				flag = 1;
				break;
			}
		}
		if (flag) continue;
		u = stk[to--];
	}
}

inline int Query(int u,int v)
{
	int ans = 0;
	while (top[u] != top[v])
	{
		if (dep[top[u]] < dep[top[v]]) swap(u,v);
		ans ^= query(1,1,n,id[top[u]],id[u]);
		u = fa[top[u]];
	}
	if (u == v) return ans ^ a[u];
	if (dep[u] > dep[v]) swap(u,v);
	return ans ^ query(1,1,n,id[u],id[v]);
}

inline char getcom()
{
	char c = getchar();
	while (c != 'Q' && c != 'C') c = getchar();
	return c;
}

inline int getint()
{
	int ret = 0;
	char c = getchar();
	while (c < '0' || '9' < c) c = getchar();
	while ('0' <= c && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret;
}

int main()
{
	n = getint();
	for (int i = 1; i <= n; i++) a[i] = getint();
	for (int i = 1; i <= n - 1; i++)
	{
		int u = getint(),v = getint();
		e[u].push_back(v);
		e[v].push_back(u);
	}
	bfs_init();
	memset(b,0,sizeof(b));
	memset(cur,0,sizeof(cur));
	top[1] = 1;
	to = 0;
	w[id[1] = ++tot] = a[1];
	bfs_cut();
	build(1,1,n);
	q = getint();
	for (int i = 1; i <= q; i++)
	{
		char c = getcom(); int x = getint(),y = getint();
		if (c == 'Q')
		{
			if (Query(x,y)) printf("Yes\n");
			else printf("No\n");
		}
		else modify(1,1,n,id[x],a[x] = y);
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值