「洛谷3292」「BZOJ4568」「SCOI2016」幸运数字【倍增LCA+线性基+合并】

【bzoj数据下载地址】不要谢我


先讲一下窝是怎么错的。。。
M L E MLE MLE是因为数组开小了。。


看到异或和最大,那么就会想到用线性基。
如果不会线性基的可以参考一下我的学习笔记:「线性基」学习笔记and乱口胡总结


但是这一道题目需要合并线性基。
如何合并线性基?
不需要什么花里胡哨的操作,直接暴力插入就可以了。

void merge(xxj &x, xxj y) {
	for (int i = BIT; ~i; i --) 
		if (y.p[i]) x.ins(y.p[i]);
}

代码中的 x x x y y y是两个线性基。
原理就是把 y y y中的每一个元素插入到 x x x中。
然后再套一个倍增求 L C A LCA LCA就可以了。

代码

#include <bits/stdc++.h>
#define gc getchar
using namespace std;
typedef long long ll;
const int BIT = 62;
const int LOG = 21; 
const int N = 40005;
template <typename T> void read(T &x) {
	x = 0; T fl = 1; char c = 0;
	for (; c < '0' || c > '9'; c = gc()) 
		if (c == '-') fl = -1;
	for (; c >= '0' && c <= '9'; c = gc()) 
		x = (x << 1) + (x << 3) + (c ^ 48); 
	x *= fl; 
}
struct xxj {
	ll p[BIT + 2];
	void clear() { memset(p, 0, sizeof(p)); }
	void ins(ll x) {
		for (int i = BIT; ~i; i --) {
			if ((x >> i) == 0) continue;
			if (!p[i]) { p[i] = x; break; }
			x ^= p[i];
		}
	}
} g[N][LOG + 2], ans;
struct edge {
	int to, nt;
} E[N];
int fa[N][LOG + 2];
int n, ecnt, Q;
int dep[N], H[N];
void add_edge(int u, int v) {
	E[++ ecnt] = (edge){v, H[u]};
	H[u] = ecnt;
}
void merge(xxj &x, xxj y) {
	for (int i = BIT; ~i; i --) 
		if (y.p[i]) x.ins(y.p[i]);
}
void dfs(int u, int ft) {
	fa[u][0] = ft; dep[u] = dep[ft] + 1;
	for (int i = 1; i <= LOG; i ++) {
		fa[u][i] = fa[fa[u][i - 1]][i - 1];
		g[u][i] = g[u][i - 1];
		merge(g[u][i], g[fa[u][i - 1]][i - 1]);
	}
	for (int e = H[u]; e; e = E[e].nt) {
		int v = E[e].to;
		if (v == fa[u][0]) continue; 
		dfs(v, u);
	}
}
void Lca(int u, int v) {
	if (dep[u] < dep[v]) swap(u, v);
	for (int i = LOG; ~i; i --) 
		if (dep[fa[u][i]] >= dep[v]) 
			merge(ans, g[u][i]), u = fa[u][i];
	if (u == v) {
		merge(ans, g[u][0]);
		return;
	}
	for (int i = LOG; ~i; i --) {
		if (fa[u][i] != fa[v][i]) {
			merge(ans, g[u][i]); 
			merge(ans, g[v][i]); 
			u = fa[u][i]; v = fa[v][i]; 
		}
	}
	merge(ans, g[u][0]);
	merge(ans, g[v][0]); 
	merge(ans, g[fa[u][0]][0]);
}
int main() {
	read(n); read(Q);
	for (int i = 1; i <= n; i ++) {
		ll x; read(x);
		g[i][0].ins(x);
	}
	for (int i = 1, u, v; i < n; i ++) {
		read(u); read(v); 
		add_edge(u, v);
		add_edge(v, u);
	}
	dfs(1, 0);
	while (Q --) {
		int u, v; read(u); read(v); 
		ans.clear();
		Lca(u, v); 
		ll res = 0ll;
		for (int i = BIT; ~i; i --) 
			if ((res ^ ans.p[i]) > res) res ^= ans.p[i];
		cout << res << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值