描述
You are given a tree with N nodes. The tree nodes are numbered from 1
to N. Each node has an integer weight.We will ask you to perform the following operation:
u v k : ask for the kth minimum weight on the path from node u to node
v
Input
In the first line there are two integers N and M. (N, M <= 100000)
In the second line there are N integers. The ith integer denotes the
weight of the ith node.In the next N-1 lines, each line contains two integers u v, which
describes an edge (u, v).In the next M lines, each line contains three integers u v k, which
means an operation asking for the kth minimum weight on the path from
node u to node v.
Output
For each operation, print its result.
Input:
8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
2 5 2
2 5 3
2 5 4
7 8 2
Output:
2
8
9
105
7
思路
给你n个节点,每个节点对应有权值,然后给你n-1条边,使之形成一棵树,然后有m组询问,每次询问u v k,求从u点到v点的第k小值.
首先这是一棵树,不是区间,所以不能用主席树直接求出两点之间路径的第k大。本来想用树链剖分搞一搞,但是想到虽然剖分成了若干条链,但是这两个节点不一定在一条链上,所以我们要换一种思路。
我们可以用熟练剖分求助这两个点的lca,然后构建一棵主席树,但是这个主席树的构建方法和之间构造区间不一样,之前的区间构造方法是[1,i]前缀和的形式构造,那么对于这个题我们可以对每一个节点到根节点建立前缀和,就能找任意个节点到根节点的第K值,
那么根据主席树的性质,我们就能够计算(u,v)的路上的第K值了
考虑对于任意两个点u,vu,v,现在已经构造出了一棵主席树,前缀和存储的是根节点到该节点,那么我们我们就把从根节点到u和从根节点到v的路径加起来,这个过程肯定存在重复计算,我们要把重复的路径减去,重复的路径是从根节点到lca对应的线段树,但是我们要保留其中一个lca,所以就是计算出:
sum[u]+sum[v]-sum[lca]-sum[fa[lca]]
这一棵线段树的第k大,直接求出就好
代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <sstream>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
#include <list>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll;
const int N = 5e5 + 10;
const int inf = 0x3f3f3f3f;
int n, m;
int fa[N], dep[N], siz[N], son[N], top[N], w[N];
int first[N], tot;
int b[N], p;
int sum[N << 5], rt[N], lc[N << 5], rc[N << 5], node_cnt;
struct edge
{
int v, next;
} e[N * 2];
void init()
{
mem(first, -1);
tot = 0;
node_cnt = 0;
}
void add_edge(int u, int v)
{
e[tot].v = v;
e[tot].next = first[u];
first[u] = tot++;
}
int qlca(int x, int y)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
void build(int &t, int l, int r)
{
t = ++node_cnt;
if (l == r)
return;
int mid = (l + r) >> 1;
build(lc[t], l, mid);
build(rc[t], mid + 1, r);
}
int modify(int o, int l, int r)
{
int oo = ++node_cnt;
lc[oo] = lc[o];
rc[oo] = rc[o];
sum[oo] = sum[o] + 1;
if (l == r)
return oo;
int mid = (l + r) >> 1;
if (p <= mid)
lc[oo] = modify(lc[oo], l, mid);
else
rc[oo] = modify(rc[oo], mid + 1, r);
return oo;
}
int query(int u, int v, int lca, int flca, int l, int r, int k)
{
int mid = (l + r) >> 1, ans;
int x = sum[lc[u]] + sum[lc[v]] - sum[lc[lca]] - sum[lc[flca]];
if (l == r)
return b[l];
if (x >= k)
ans = query(lc[u], lc[v], lc[lca], lc[flca], l, mid, k);
else
ans = query(rc[u], rc[v], rc[lca], rc[flca], mid + 1, r, k - x);
return ans;
}
void dfs1(int u, int f, int deep)
{
fa[u] = f;
dep[u] = deep;
siz[u] = 1;
son[u] = 0;
int maxson = -1;
for (int i = first[u]; ~i; i = e[i].next)
{
int v = e[i].v;
if (v == f)
continue;
dfs1(v, u, deep + 1);
siz[u] += siz[v];
if (siz[v] > maxson)
{
son[u] = v;
maxson = siz[v];
}
}
}
int q;
void dfs2(int u, int topf)
{
top[u] = topf;
p = w[u]; //在这个地方建立主席树
rt[u] = modify(rt[fa[u]], 1, q);
if (!son[u])
return;
dfs2(son[u], topf);
for (int i = first[u]; ~i; i = e[i].next)
{
int v = e[i].v;
if (v == fa[u] || v == son[u])
continue;
dfs2(v, v);
}
}
int main()
{
// freopen("in.txt", "r", stdin);
int u, v, k;
scanf("%d%d", &n, &m);
init();
for (int i = 1; i <= n; i++)
{
scanf("%d", &w[i]);
b[i] = w[i];
}
sort(b + 1, b + n + 1);
for (int i = 1; i <= n - 1; i++)
{
scanf("%d%d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}
q = unique(b + 1, b + n + 1) - (b + 1);
//计算出新的节点的标号
for (int i = 1; i <= n; i++)
w[i] = lower_bound(b + 1, b + q + 1, w[i]) - b;
build(rt[0], 1, q);
dfs1(1, 0, 1);
dfs2(1, 1);
while (m--)
{
scanf("%d%d%d", &u, &v, &k);
int lca = qlca(u, v);
int flca = fa[lca];
int ans = query(rt[u], rt[v], rt[lca], rt[flca], 1, q, k);
printf("%d\n", ans);
}
return 0;
}

本文介绍如何利用树链剖分与主席树解决特定树形结构上的路径查询问题,重点在于如何通过树链剖分找到最近公共祖先,并构建主席树以高效查询两点间路径上的第k小权值。
1717

被折叠的 条评论
为什么被折叠?



