洛谷P5556 圣剑护符

题目链接

先考虑如何判定一个集合是否存在两个异或和相同的子集 \(s,t\),不然解决这道题就是无稽之谈。

根据异或的优良性质,不妨在 \(s,t\) 中分别去掉 \(s\cap t\),之后从 \(s\) 中任意移动 \(|s|-1\) 个元素到 \(t\) 中去,易发现此时两个集合的元素异或和还是相同。也就是说,我们现在只需要逐个判断当前元素能否被其他元素异或出来即可。

于是我们构造一个线性基,逐个插入元素,插入的时候判断一下:如果插入不成功,则说明当前元素可以被异或出来,输出 YES

但在给出的集合数以及集合大小都为 \(10^5\) 的情况下该如何处理?我们再尝试寻找一些性质:根据线性基的大小不超过 \(\log V\),不难得出我们插入成功也必定不超过 \(\log V\) 次。也就是说,如果集合大小 \(>\log V\),直接输出 YES 即可。

再回到原问题。这是一个树上带修问题——使用树链剖分解决即可。树剖中可以不写线段树,只需要写一个区改单查的树状数组。代码细节不多,较为好写。

点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
const int N = 1e5 + 10, W = 29;
namespace T{
	int n, tot, d[N], fa[N], sz[N], tp[N], son[N], id[N];
	vector<int> e[N];
	namespace BIT{
		int c[N];
		void Add(int x, int v){
			for(; x <= n; x += x & -x) c[x] ^= v;
		}
		int Ask(int x, int r = 0){
			for(; x; x -= x & -x) r ^= c[x]; return r;
		}
	}
	void Dfs1(int u){
		d[u] = d[fa[u]] + (sz[u] = 1);
		for(const int &v: e[u]) if(v != fa[u]){
			fa[v] = u, Dfs1(v), sz[u] += sz[v];
			if(sz[v] > sz[son[u]]) son[u] = v;
		}
	}
	void Dfs2(int u){
		id[u] = ++tot, tp[u] = (son[fa[u]] == u? tp[fa[u]] : u);
		if(son[u]) Dfs2(son[u]);
		for(const int &v: e[u])
			if(v != fa[u] && v != son[u])
				Dfs2(v);
	}
	int Lca(int x, int y){
		while(tp[x] != tp[y]){
			if(d[tp[x]] < d[tp[y]]) swap(x, y);
			x = fa[tp[x]];
		}
		return d[x] < d[y]? x : y;
	}
	void Upd(int x, int y, int z){
		while(tp[x] != tp[y]){
			if(d[tp[x]] < d[tp[y]]) swap(x, y);
			BIT::Add(id[tp[x]], z);
			BIT::Add(id[x] + 1, z), x = fa[tp[x]];
		}
		if(d[x] < d[y]) swap(x, y);
		BIT::Add(id[y], z), BIT::Add(id[x] + 1, z);
	}
	int Qry(int x){return BIT::Ask(id[x]);}
}
using namespace T;
struct LinearBasis{
	int a[W + 2];
	void init(){fill(a, a + W + 1, 0);}
	int insert(int x){
		FR(i, W, 0) if((x >> i) & 1){
			if(!a[i]){a[i] = x; return 1;}
			else x ^= a[i];
		}
		return 0;
	}
} b;
int q, a[N];
int main(){
	scanf("%d%d", &n, &q);
	char op[10]; int x, y, z, lca, u, flag;
	FL(i, 1, n) scanf("%d", &a[i]);
	FL(i, 2, n){
		scanf("%d%d", &x, &y);
		e[x].emplace_back(y), e[y].emplace_back(x);
	}
	Dfs1(1), Dfs2(1); FL(i, 1, n) Upd(i, i, a[i]);
	while(q--){
		scanf("%s%d%d", op, &x, &y);
		if(op[0] == 'U') scanf("%d", &z), Upd(x, y, z);
		else{
			lca = Lca(x, y), b.init();
			flag = (d[x] + d[y] - d[lca] - d[fa[lca]] <= 30);
			for(u = x; flag && u != lca; u = fa[u])
				if(!b.insert(Qry(u))) flag = 0;
			for(u = y; flag && u != fa[lca]; u = fa[u])
				if(!b.insert(Qry(u))) flag = 0;
			printf(flag? "NO\n" : "YES\n");
		}
	}
	return 0;
}
posted @ 2023-09-09 12:00  徐子洋  阅读(105)  评论(0)    收藏  举报