小红的树上路径查询(hard)

小红的树上路径查询(hard)

题目描述

本题和 hard 难度的区别是,询问的次数有多次!

小红拿到了一棵树,她有多次询问,每次询问输入一条简单路径 x,y,她想知道树上所有节点到该路径的最短路之和是多少,你能帮帮她吗?

定义节点到路径的最短路为:节点到路径上所有点的最短路中,值最小的那个。特殊的,如果节点在路径上,则最短路为 0

简单路径:从树上的一个节点出发,沿着树的边走,不重复地经过树上的节点,到达另一个节点的路径。

输入描述:

第一行输入两个正整数 n,q,代表节点数量和询问次数。

接下来的 n1 行,每行输入两个正整数 u,v,代表节点 u 和节点 v 有一条边连接。

接下来的 q 行,每行输入两个正整数 x,y,代表一次询问。

1n,q105

1u,v,x,yn

输出描述:

输出 q 行,每行输出一个整数,代表询问的答案。

示例1

输入

4 2
1 2
1 3
1 4
2 3
2 1

输出

1
2

 

解题思路

  不会,直接参考的题解。

  对于 xy 构成的 xy 路径外一点 v,假设 vxy 路径上最近的点是 u,那么 vxvy 的路径必定包含点 u(假设以 v 为根进行 bfs,那么第一次遍历到 xy 路径上的点就是 u,继而从 u 扩展到 xy 路径上的其他点)。这点其实还是很难想到的。

  然后求 v 分别到 xy 距离之和。有

d(v,x)+d(v,y)=d(v,u)+d(u,x)+d(v,u)+d(u,y)=2d(v,u)+d(u,x)+d(u,y)=2d(v,u)+d(x,y)

  即有 d(v,x)+d(v,y)=2d(v,u)+d(x,y)d(v,u)=d(v,x)+d(v,y)d(x,y)2。这条式子有什么用呢?实际上我们关心的是所有点到 u(即该点到 xy 路径最近的点)的距离的和,并不需要求出具体的 u。同时可以发现如果 v 也是 xy 路径上的点上式同样成立。因此所有点关于 d(v,u) 的和就是

v=1nd(v,u)=12v=1nd(v,x)+d(v,y)d(x,y)=12(v=1nd(v,x)+v=1nd(v,y)nd(x,y))

  其中 v=1nd(v,x)v=1nd(v,y) 分别是 xy 到所有点的距离总和,这个可以用换根 dp 求得。d(x,y) 可以分别求出 xy 到最近公共祖先的距离再求和。

  下面简单讲一下如何求所有点到 u 的距离总和。固定 1 为根,定义 f(u) 表示子树 u 中所有点到 u 的距离总和,g(u) 表示从 u 往上走的所有点(其实就是除了子树 u 外的点)到 u 的距离总和。那么状态转移方程就是

f(u)=vson(u)f(v)+szv

g(u)=g(pu)+f(pu)(f(u)+szu)+(nszu)

  其中 szu 表示子树 u 的大小,pu 表示 u 的父节点。那么所有点到 u 的距离总和就是 f(u)+g(u)

  AC 代码如下,时间复杂度为 O((n+q)logn)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 1e5 + 5, M = N * 2;

int n, m;
int h[N], e[M], ne[M], idx;
LL sz[N], f[N], g[N];
int fa[N][17], d[N];

void add(int u, int v) {
    e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}

void dfs1(int u, int p) {
    sz[u] = 1;
    d[u] = d[p] + 1;
    fa[u][0] = p;
    for (int i = 1; i <= 16; i++) {
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    }
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if (v == p) continue;
        dfs1(v, u);
        sz[u] += sz[v];
        f[u] += f[v] + sz[v];
    }
}

void dfs2(int u, int p) {
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if (v == p) continue;
        g[v] = g[u] + f[u] - (f[v] + sz[v]) + n - sz[v];
        dfs2(v, u);
    }
}

int lca(int a, int b) {
    if (d[a] < d[b]) swap(a, b);
    for (int i = 16; i >= 0; i--) {
        if (d[fa[a][i]] >= d[b]) a = fa[a][i];
    }
    if (a == b) return a;
    for (int i = 16; i >= 0; i--) {
        if (fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
    }
    return fa[a][0];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> m;
    memset(h, -1, sizeof(h));
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v), add(v, u);
    }
    dfs1(1, 0);
    dfs2(1, 0);
    while (m--) {
        int x, y;
        cin >> x >> y;
        cout << (f[x] + g[x] + f[y] + g[y] - n * (d[x] + d[y] - 2 * d[lca(x, y)])) / 2 << '\n';
    }
    
    return 0;
}

 

参考资料

  牛客周赛64题解:https://ac.nowcoder.com/discuss/1421788

posted @   onlyblues  阅读(104)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2023-10-23 D2. Dances (Hard Version)
2023-10-23 B. Raspberries
2023-10-23 C. Medium Design
2022-10-23 A. Bestie
2022-10-23 三元组
Web Analytics
点击右上角即可分享
微信分享提示