P2420 让我们异或吧-异或性质-树形结构

版权声明:看我干嘛? 你又没打算转载我的博客~ https://blog.csdn.net/wjh2622075127/article/details/82622715
题目链接

题目描述

异或是一种神奇的运算,大部分人把它总结成不进位加法.

在生活中…xor运算也很常见。比如,对于一个问题的回答,是为1,否为0.那么:

(A是否是男生 )xor( B是否是男生)=A和B是否能够成为情侣

好了,现在我们来制造和处理一些复杂的情况。比如我们将给出一颗树,它很高兴自己有N个结点。树的每条边上有一个权值。我们要进行M次询问,对于每次询问,我们想知道某两点之间的路径上所有边权的异或值。

输入输出格式

输入格式:

输入文件第一行包含一个整数N,表示这颗开心的树拥有的结点数,以下有N-1行,描述这些边,每行有3个数,u,v,w,表示u和v之间有一条权值为w的边。接下来一行有一个整数M,表示询问数。之后的M行,每行两个数u,v,表示询问这两个点之间的路径上的权值异或值。

输出格式:

输出M行,每行一个整数,表示异或值

输入输出样例

输入样例#1: 复制
5
1 4 9644
2 5 15004
3 1 14635
5 3 9684
3
2 4
5 4
1 1
输出样例#1: 复制
975
14675
0

说明

对于40%的数据,有1 ≤ N,M ≤ 3000;

对于100%的数据,有1 ≤ N ,M≤ 100000。



这道题看着很可怕,又是异或又是树什么的。鉴于异或的特殊性,我google了下异或是否有逆运算,了解了这些公式:

a ^ 0 = a
a ^ a = 0
a ^ b = c -> a = b ^ c -> b = a ^ c

于是我就想,如果我们求的图形不是树,而是简单的链状结构,那么结合上面的公式,岂不是可以很简单地用前缀异或与差分的思想来解决?

但这是树啊,于是我另辟蹊径。

也许是树链剖分?也许要用LCA?然而我都不会。

但是!解题的关键恰恰是看似把题目复杂化了的xor运算!此题的最简单解法就是用前缀异或与差分的思想


异或运算xor满足交换律和结合律。

以任意结点为根节点,从该节点遍历其他结点,算出路径上边权的异或值。那么考虑这种一般情况,结点4和结点5有最近公共祖先(LCA)结点3,那么根节点1到4的异或值为X1,4, 1到5的异或值为X1,5, 1到3的异或值为X1,3,我们要求的是从结点4到5的异或值。

有如下几个式子:


X1,3 ^ X3,4 = X1,4 -> X3,4 = X1,3 ^ X1,4
X1,3 ^ X3,5 = X1,5 -> X3,5 = X1,3 ^ X1,5
X4,5 = X3,4 ^ X3,5 = X1,3 ^ X1,4 ^ X1,3 ^ X1,5 = X1,4 ^ X1,5

于是,要求出从结点4到结点5的异或值,只要异或从根结点到结点4和结点5的异或值。

#include <iostream>
#include <vector>
using namespace std;

struct Node {
    int to, cost;
    int xo;
    Node (int t, int c): to(t), cost(c) {}
};

const int maxn = 100005;
int n, m;
vector<Node> G[maxn];
int xo[maxn] = {};
bool vis[maxn] = {};

void dfs(int u)
{
    for (size_t i = 0; i < G[u].size(); ++i) {
        int to = G[u][i].to, c = G[u][i].cost;
        if (!vis[to]) {
            xo[to] = xo[u] ^ c;
            vis[to] = true;
            dfs(to);
        }
    }
}

int main()
{
    cin >> n;
    for (int i = 1; i < n; ++i) {
        int u, v, c;
        cin >> u >> v >> c;
        G[u].push_back(Node(v, c));
        G[v].push_back(Node(u, c));
    }
    vis[1] = true;
    dfs(1);
    cin >> m;
    for (int i = 1; i <= m; ++i) {
        int x, y;
        cin >> x >> y;
        cout << (xo[x] ^ xo[y]) << endl;
    }
}

猜你喜欢

转载自blog.csdn.net/wjh2622075127/article/details/82622715