HDU 4757 :Tree(树上可持久化trie)

在这里插入图片描述
题目大意:给一颗带点权的无向树,每次询问 (x,y,v) :x,y 的路径上 找一个点和 v 异或,使得异或出来的权值最大,问最大是多少。

和树上主席树一样套路,按dfs序建可持久化trie,求出 x,y 的 lca ,减一下求出 x,y 路径的 trie就行


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 10;
int n,m;
int root[maxn * 2],a[maxn * 2],sz;				//trie部分 
int lca(int u,int v);
int p[maxn][22],dep[maxn];
vector<int> g[maxn];
int lca(int u,int v);
struct trie{									//可持久化字典树 
	int sz,son[maxn * 40][2],sum[maxn * 40];
	void init() {
		sz = 0;
	}
	inline void update(int& root,int las,int val){
	    int now=root=++sz;
	    sum[now]=sum[las]+1;
	    for(int i=18;i >= 0;--i){
	    	int p = (val >> i) & 1;
	        son[now][p ^ 1]=son[las][p^1],
	        son[now][p]=++sz,las=son[las][p],
	        now=sz,sum[now]=sum[las]+1;
	    }
	}
	void upd(int &rt,int v) {					//类似主席树的更新,每一步都要更新一个结点 
		++sz;
		son[sz][0] = son[rt][0];
		son[sz][1] = son[rt][1];
		sum[sz] = sum[rt] + 1;
		rt = sz;
		int t = rt;
		for(int i = 18; i >= 0; i--) {
			int p = (v >> i) & 1;
			++sz;
			int s = son[t][p];
			sum[sz] = sum[s] + 1;
			son[sz][0] = son[s][0];
			son[sz][1] = son[s][1];
			son[t][p] = sz;
			t = son[t][p];
		}
	}
	int qry(int x,int y,int lca,int v) {
		int ans = 0,flca = p[lca][0];
		int rx = root[x],ry = root[y],rlca = root[lca];
		int rf = root[flca];
		for(int i = 18; i >= 0; i--) {
			int p = (v >> i) & 1;
			if(sum[son[rx][p ^ 1]] + sum[son[ry][p ^ 1]] - sum[son[rlca][p ^ 1]] - sum[son[rf][p ^ 1]] > 0) {
				ans |= (1 << i);
				rx = son[rx][p ^ 1];
				ry = son[ry][p ^ 1];
				rlca = son[rlca][p ^ 1];
				rf = son[rf][p ^ 1];
			} else {
				rx = son[rx][p];
				ry = son[ry][p];
				rlca = son[rlca][p];
				rf = son[rf][p];
			}
		}	
		return ans;
	}
}trie;
int dfs(int u,int fa) {
	dep[u] = dep[fa] + 1;
	root[u] = root[fa];
	trie.upd(root[u],a[u]);
	for(int i = 1; i <= 20; i++)
		p[u][i] = p[p[u][i - 1]][i - 1];
	for(auto v : g[u]) {
		if(v == fa) continue;
		p[v][0]	= u;
		dfs(v,u);
	}
}
int lca(int u,int v) {
	if(dep[u] < dep[v]) swap(u,v);
	for(int i = 20; i >= 0; i--) {
		if(dep[p[u][i]] >= dep[v]) 
			u = p[u][i];
	}
	if(u == v) return u;
	for(int i = 20; i >= 0; i--) {
		if(p[u][i] != p[v][i]) {
			u = p[u][i];
			v = p[v][i];
		}
	}
	return p[u][0];
}
int main() {
	while(~scanf("%d%d",&n,&m)) {
		trie.init();
		for(int i = 1; i <= n; i++) {
			scanf("%d",&a[i]);
			g[i].clear();
		}
		for(int i = 1,u,v; i < n; i++) {
			scanf("%d%d",&u,&v);
			g[u].push_back(v);
			g[v].push_back(u);
		}
		dfs(1,0);
		for(int i = 1,x,y,z; i <= m; i++) {
			scanf("%d%d%d",&x,&y,&z);
			printf("%d\n",trie.qry(x,y,lca(x,y),z));
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值