题面
给出N<=100000节点的树,边上有权值,对于M<=100000次询问,输出其路径上所有权值的异或
分析
这题简单的做法其实是预处理到根的前缀,然后对于查询直接异或两个到root的权值即可。。。(加减法,在异或中都是异或),所以才是普及+级别。。。
先把运算当作加法:
u到v的路径即u→lca→v,延长这个路径,u→lca→root→lca→v,加减法情形,可以预处理root→lca,从而将重复段删除两次,这样发现对于这种问题只要求出所有节点到root的权值即可
对于异或,甚至更简单,u→root的权值xorv→root的权值就直接等于u→v的权值异或,这是因为重复段的异或会得0.
即u→lca→root→lca
→v,红色部分在异或下得0,所以处理出所有节点到根的异或,查询时直接两者异或即可。
而这里一开始我被树链剖分误导了。。。。放了树链剖分的板子上去,但是结果也不差,最终用线性前缀和维护树链剖分得到的片段即可
和求LCA相比,多的部分就是,通过换dfs序,将重链上的数据都放在了一起,便于区间维护,加上轻重链均最多logn段,就做到了单次查询O(logn)的复杂度
取出这些链的操作详见下面calxor函数,将异或换为加法等其他运算就可以改变作用,但取链的方式仍然没变
代码
预处理:将线段上的权值放到节点上,节点的选择优先层深的(即最后会导致根上无权值,边上的权值都放到更向下的点上)
#include<iostream>
#include<vector>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<string>
using namespace std;
int A[100005];//用前缀和维护的数组
int preA[100005];//A前缀和
int B[100005];//原数组,经过dfs序映射后得到A
void sswap(int& a, int& b)
{
int t = b;
b = a;
a = t;
}
class Tree_Chain {
private:
const static int MAXN = 100005;
int siz[MAXN];//表示其子树的节点数
int fa[MAXN];//当前节点的父节点
int son[MAXN];//节点的重儿子
int top[MAXN];//重链顶
int dep[MAXN];//当前节点深
int l[MAXN];//dfs序
std::vector<pair<int, int> >linker[MAXN];//存顶点存权值,后面的> >间要有空格
public:
int dfs_clock = 0;
void dfs1(int x)//x是当前节点(当前树根)
{
int cur;
siz[x] = 1;
for (int i = 0; i < linker[x].size(); i++)
{
cur = linker[x][i].first;
if (cur != fa[x]) //不是fa
{
B[cur] = linker[x][i].second;//预处理,将边权放到点编号上
dep[cur] = dep[x] + 1;//更新dep
fa[cur] = x;//更新fa
dfs1(cur);
siz[x] += siz[cur];//更新siz
if (siz[cur] > siz[son[x]])son[x] = cur;//更有可能成为重儿子
}
}
}
void dfs2(int x, int t)//当前节点x,当前重链顶的编号
{
l[x] = ++dfs_clock;//更新dfs序
A[dfs_clock] = B[x];//换序映射,使重链节点在A上连续,A用前缀和维护
top[x] = t;
if (son[x])dfs2(son[x], t);//继续连重链
int cur;
for (int i = 0; i < linker[x].size(); i++)
{
cur = linker[x][i].first;
if (cur != fa[x] && cur != son[x])//非父亲非重儿子
dfs2(cur, cur);//在其他儿子上找更小一些的
}
}
void insert(int& u, int& v, int& w)
{
linker[u].push_back(make_pair(v, w));
linker[v].push_back(make_pair(u, w));
}
int Lca(int& u, int& v)
{
while (top[u] != top[v])
{
if (dep[top[u]] > dep[top[v]])u = fa[top[u]];
else v = fa[top[v]];
}
return dep[u] < dep[v] ? u : v;
}
int calxor(int& u, int& v)//取轻/重链并在区间上处理
{
int ans = 0;
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])sswap(u, v);
ans ^=( preA[l[u]] ^ preA[l[top[u]] - 1]);//l[top[u]]到l[u]是dfs序上连续的一段
u = fa[top[u]];
}
if (dep[u] > dep[v])sswap(u, v);//到了一个重链上,切换到u比v浅的状态
ans ^=( preA[l[v]] ^ preA[l[u]]);
return ans;
}
}TC;
int main()
{
ios::sync_with_stdio(false);
int n, u, v, temp, q;
cin >> n;
for (int i = 1; i < n; i++)
{
cin >> u >> v >> temp;
TC.insert(u, v, temp);
}
TC.dfs1(1);
TC.dfs2(1, 1);
for (int i = 2; i <= n; i++)
{
preA[i] = preA[i - 1] ^ A[i];
}
cin >> q;
for (int i = 0; i < q; i++)
{
cin >> u >> v;
cout << TC.calxor(u, v) << endl;
}
return 0;
}