2019 icpc西安邀请赛 E. Tree【树链剖分+区间异或和】

36 篇文章 0 订阅
20 篇文章 0 订阅

传送门

求树上简单路径的异或和【树链剖分】+【线段树】
支持,修改链上的值为 a i ∣ t a_i|t ait 以及 a i & t a_i\&t ai&t

分析

对位考虑,每一位建立一颗线段树,维护链上值的位情况

  • 假如是操作 1 1 1 a i ∣ t a_i|t ait
    只有当 t t t的某一位为 1 1 1的时才会对线段树进行更新,而且是区间 s e t set set 1 1 1

  • 假如是操作 2 2 2 a i & t a_i\&t ai&t
    只有当 t t t的某一位为 0 0 0的时才会对线段树进行更新,而且是区间 s e t set set 0 0 0

细节:
由于异或性质,长度为偶数的区间 s e t set set 1还是0,奇数才会有影响
这里要求的是是否能赢,而且是经典的 n i m nim nim博弈(不会的可以搜一下),只需要求链上的异或和与 t t t异或后是否为零即可

简单说一下, n i m nim nim博弈任取石子的情况就是,所有堆二进制下的异或值是否为 0 0 0
0 0 0代表,只要先手执行一种操作,后手一定能够模仿这种操作,取这么多的石子,因为假如前者取的是 x x x个,二进制拆分为 010110 010110 010110,由于全部值异或为 0 0 0(全部二进制位都出现了偶数次),必然会有一种取法能够满足二进制拆分也是这样 010110 010110 010110

代码

//jsk39272-solve
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 5e5+5;
const ll MOD = 1e9+7;
int N, M, K;

int arr[MAX_N];

int head[MAX_N];
int tot = 0;
struct Edge{
	int to, nxt;
}edge[MAX_N];

void addEdge(int u, int v) {
	edge[tot].nxt = head[u];
	edge[tot].to = v;
	head[u] = tot++;
	edge[tot].nxt = head[v];
	edge[tot].to = u;
	head[v] = tot++;
}

int top[MAX_N];
int father[MAX_N];
int dep[MAX_N];
int sz[MAX_N];
int son[MAX_N];
int dfn[MAX_N];
int sa[MAX_N];
int dfncnt = 0;

void dfs1(int u, int from, int d) {
	sz[u] = 1;
	father[u] = from;
	dep[u] = d;
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from) continue;
		dfs1(v, u, d+1);
		sz[u] += sz[v];
		if (sz[v] > sz[son[u]]) {
			son[u] = v;
		}
	}
}


void dfs2(int u, int tp) {
	top[u] = tp;
	dfn[u] = ++dfncnt;
	sa[dfncnt] = u;
	if (son[u]) dfs2(son[u], tp);
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == son[u] || v == father[u]) continue;
		dfs2(v, v);
	}
}

struct Seg {
	struct Tr {
		int k, lazy, len;
	}tr[MAX_N];
	
	void push_up(int rt) {
		tr[rt].k = tr[rt<<1].k ^ tr[rt<<1|1].k;
	}
	
	void calc(int rt, int lazy) {
		if (lazy == -1) return;
		tr[rt].k = lazy&tr[rt].len;
		tr[rt].lazy = lazy;
	}
	
	void push_down(int rt) {
		if (tr[rt].lazy == - 1) return;
		int ls = rt << 1;
		int rs = ls|1;
		int& lazy = tr[rt].lazy;
		calc(ls, lazy);
		calc(rs, lazy);
		tr[rt].lazy = -1;
	}
	
	void build(int rt, int l, int r) {
		tr[rt].len = r - l + 1;
		tr[rt].lazy = -1;
		if (l == r) {
			tr[rt].k = arr[sa[l]]&1;
			return;
		}
		int mid = l + ((r-l)>>1);
		build(rt<<1, l, mid);
		build(rt<<1|1, mid+1, r);
		push_up(rt);
	}
	
	void update(int rt, int l, int r, int x, int y, int k) {
		if (x <= l && r <= y) {
			calc(rt, k);
			return;
		}
		push_down(rt);
		int mid = l + ((r-l)>>1);
		if (x <= mid) update(rt<<1, l, mid, x, y, k);
		if (y  > mid) update(rt<<1|1, mid+1, r, x, y, k);
		push_up(rt);
	}
	
	int query(int rt, int l, int r, int x, int y) {
		if (x <= l && r <= y) {
			return tr[rt].k;
		}
		push_down(rt);
		int mid = l + ((r-l)>>1);
		if (y <= mid) return query(rt<<1, l, mid, x, y);
		if (x  > mid) return query(rt<<1|1, mid+1, r, x, y);
		return query(rt<<1, l, mid, x, y) ^ query(rt<<1|1, mid+1, r, x, y);
	}
	
}bit[33];

void change(int x, int y, int id, int k) {
	while (top[x] != top[y]) {
		if (dep[top[x]] >= dep[top[y]]) {
			bit[id].update(1, 1, N, dfn[top[x]], dfn[x], k);
			x = father[top[x]];
		} else {
			bit[id].update(1, 1, N, dfn[top[y]], dfn[y], k);
			y = father[top[y]];
		}
	}
	if (dep[x] > dep[y]) {
		bit[id].update(1, 1, N, dfn[y], dfn[x], k);
	} else {
		bit[id].update(1, 1, N, dfn[x], dfn[y], k);
	}
}

int queryBit(int x, int y, int id) {
	int res = 0;
	while (top[x] != top[y]) {
		if (dep[top[x]] >= dep[top[y]]) {
			res ^= bit[id].query(1, 1, N, dfn[top[x]], dfn[x]);
			x = father[top[x]];
		} else {
			res ^= bit[id].query(1, 1, N, dfn[top[y]], dfn[y]);
			y = father[top[y]];
		}
	}
	if (dep[x] >= dep[y]) {
		res ^= bit[id].query(1, 1, N, dfn[y], dfn[x]);
	} else {
		res ^= bit[id].query(1, 1, N, dfn[x], dfn[y]);
	}
	return res;
}

void init() {
	memset(head, -1, sizeof head);
	tot = 0;
}

void solve(){
	init();
	sc("%d%d", &N, &M);
	for (int i = 1; i <= N; ++i) {
		sc("%d", &arr[i]);
	}
	int u, v;
	for (int i = 2; i <= N; ++i) {
		sc("%d%d", &u, &v);
		addEdge(u, v);
	}
	dfs1(1, 0, 1);
	dfs2(1, 1);
	for (int i = 0; i <= 31; ++i) {
		if (i) for (int j = 1; j <= N; ++j) arr[j] >>= 1;
		bit[i].build(1, 1, N);
	}
	int opt, s, t, j;
	for (int i = 1; i <= M; ++i) {
		sc("%d%d%d", &opt, &s, &t);
		if (opt == 1) {
			for (j = 0; j <= 31; ++j) {
				if (t>>j&1) {
					change(1, s, j, 1);
				}
			}
		} else if (opt == 2) {
			for (j = 0; j <= 31; ++j) {
				if (t>>j&1);
				else {
					change(1, s, j, 0);
				}
			}
		} else {
			for (j = 0; j <= 31; ++j) {
				if (queryBit(1, s, j) ^ (t>>j&1)) {
					puts("YES");
					break;
				}
			}
			if (j == 32) puts("NO"); 
		}
	}
	
}

signed main()
{
	#ifndef ONLINE_JUDGE
	//FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hexrt

客官,请不要给我小费!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值