📜  门| GATE CS 2018 |第 58 题(1)

📅  最后修改于: 2023-12-03 15:12:36.475000             🧑  作者: Mango

题目描述

给定一棵树 $T$,树的根节点为 $1$。现在给定一组询问,每个询问给定两个整数 $u$ 和 $v$,求树上从 $u$ 到 $v$ 的路径中,异或和为偶数的边的数量。

输入格式

第一行包含一个整数 $n$,表示树的节点数。

接下来 $n-1$ 行,每行包含两个整数 $u$ 和 $v$,表示一条无向边 $(u,v)$。

接下来一行,包含一个整数 $q$,表示询问数量。

接下来 $q$ 行,每行包含两个整数 $u$ 和 $v$,表示一组询问。

输出格式

对于每个询问,输出一行整数,表示路径中异或和为偶数的边的数量。

数据范围

$1≤n≤10^5$

$1≤q≤10^5$

$1≤u,v≤n$

题解思路

对于一条路径的异或和为偶数,等价于这个路径上相邻的两个节点的异或和为偶数,即 $u \oplus v$ 为偶数。

如果考虑一条简单路径 $(1 \rightarrow u \rightarrow v)$,那么它的异或和可以表示为:$1 \oplus dep[u] \oplus dep[v] \oplus u \oplus v$,其中 $dep[x]$ 表示 $x$ 节点的深度。

如果将问题转化为计算一条简单路径的异或和为偶数的边的数量,可以将 $1$ 到 $u$ 的路径和 $1$ 到 $v$ 的路径分别使用前缀异或和求出。如果前缀异或和的异或和为偶数,那么说明这条路径为偶数路径,否则为奇数路径。

代码实现

使用前缀异或和和哈希映射优化求解时间复杂度

#include <iostream>
#include <cstdio>
#include <cstring>
#include <unordered_map>
using namespace std;

typedef long long LL;

const int N = 100010, M = N * 2;

int n, cnt;
int h[N], e[M], ne[M];
int dep[N], stk[N], top;
LL ans[N];
unordered_map<int, LL> hash[N];

void add(int a, int b)
{
    e[cnt] = b, ne[cnt] = h[a], h[a] = cnt ++ ;
}

void dfs(int u, int father)
{
    dep[u] = dep[father] + 1;
    hash[u][0] = 1;
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (j == father) continue;
        dfs(j, u);
        ans[0] += hash[u][dep[j] & 1 ^ 1] * hash[j][0] + hash[j][dep[u] & 1 ^ 1] * hash[u][0];
        if (hash[j].size() > hash[u].size())
        {
            for (auto& [k, v]: hash[u])
                ans[k ^ dep[j] ^ dep[u] ^ 1] += v * hash[j][k];
            swap(hash[u], hash[j]);
        }
        for (auto [k, v]: hash[j]) hash[u][k ^ dep[j] ^ dep[u]] += v;
    }
}

int main()
{
    scanf("%d", &n);
    memset(h, -1, sizeof h);
    for (int i = 1; i < n; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);
        add(b, a);
    }
    dfs(1, 0);
    int q;
    scanf("%d", &q);
    while (q -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        printf("%lld\n", ans[dep[a] ^ dep[b] & 1]);
    }
    return 0;
}

代码中使用了哈希映射来优化时间复杂度,具体参考了这个博客:

题解 | GATE CS 2018 | 58. Count Even Length Paths in a Tree - C++ 代码+优化 - shanrenshui891018 - 博客园