Luogu P7880 [Ynoi2006] rldcot

题目描述

https://www.luogu.com.cn/problem/P7880

简要题意:给定一棵以 $1$ 为根的 $n$ 个点的有根树,边有边权,现在有 $m$ 个询问,每次询问给出 $[l,r]$,求对于 $l\le i\le j\le r$,有多少不同的 $dep(lca(i,j))$,其中 $dep(x)$ 表示 $x$ 到根的路径权值和

$n\le 10^5,m\le 5\times 10^5$

Solution

首先对于询问我们肯定要离线下来,那么我们可以将问题转换为我们有 $\frac{n(n-1)}{2}$ 个有色区间,每次给定一个大区间求包含的子区间有多少种颜色

这是一个经典的问题,但是区间的数量太大,我们考虑减少区间的数量,对于一个点 $u$,我们考虑以它为 $lca$ 的区间中,如果存在 $[x,y],[y,z],[x,z],x<y<z$,那么 $[x,z]$ 一定是没用的,也就是说对于点 $u$ 的儿子 $v$ 的子树内的点 $w$,我们只需要找 $w$ 在 $u$ 的其它儿子的子树内的前驱和后继即可,这个东西和 $dsu$ 一样,总量是 $O(n\log n)$ 的

找到这些区间后直接离线处理即可,时间复杂度 $O(n\log^2 n+q\log n)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#define maxn 100010
#define maxm 500010
#define ll long long
#define lowbit(i) ((i) & (-i))
using namespace std;

int n, m;

int Bit[maxn];
void add(int i, int v) { while (i) Bit[i] += v, i -= lowbit(i); }
int get_sum(int i) {
int s = 0;
while (i <= n) s += Bit[i], i += lowbit(i);
return s;
}

struct Edge {
int to, next, w;
} e[maxn * 2]; int c1, head[maxn];
inline void add_edge(int u, int v, int w) {
e[c1].to = v; e[c1].w = w;
e[c1].next = head[u]; head[u] = c1++;
}

int in[maxn], out[maxn], bl[maxn], son[maxn], sz[maxn], c[maxn]; ll dep[maxn];
void pre(int u, int fa) {
static int cnt = 0;
in[u] = ++cnt; bl[cnt] = u; int Max = 0; sz[u] = 1;
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to, w = e[i].w; if (v == fa) continue;
dep[v] = dep[u] + w; pre(v, u); sz[u] += sz[v];
if (sz[v] > Max) Max = sz[v], son[u] = v;
} out[u] = cnt;
}

ll b[maxn]; int cnt;
void init_hash() {
for (int i = 1; i <= n; ++i) b[i] = dep[i];
sort(b + 1, b + n + 1); cnt = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; ++i) c[i] = lower_bound(b + 1, b + cnt + 1, dep[i]) - b;
}

set<int> S;
void add(int u) {
for (int i = in[u]; i <= out[u]; ++i) S.insert(bl[i]);
}

void del(int u) {
for (int i = in[u]; i <= out[u]; ++i) S.erase(bl[i]);
}

vector<pair<int, int>> A[maxn];
void dfs(int u, int fa) {
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to; if (v == fa || v == son[u]) continue;
dfs(v, u); del(v);
}
if (son[u]) dfs(son[u], u);
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to; if (v == fa || v == son[u]) continue;
for (int j = in[v]; j <= out[v]; ++j) {
auto It = S.upper_bound(bl[j]);
if (It != S.end()) A[*It].emplace_back(bl[j], c[u]);
if (It != S.begin()) {
auto it = prev(It);
A[bl[j]].emplace_back(*it, c[u]);
}
} add(v);
}
A[u].emplace_back(u, c[u]); S.insert(u);
}

vector<pair<int, int>> B[maxn];
int ans[maxm], pos[maxn];

int main() { fill(head, head + maxn, -1);
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> m;
for (int i = 1; i < n; ++i) {
int x, y, z; cin >> x >> y >> z;
add_edge(x, y, z); add_edge(y, x, z);
} pre(1, 0); init_hash(); dfs(1, 0);
for (int i = 1; i <= m; ++i) {
int x, y; cin >> x >> y;
B[y].emplace_back(x, i);
}
for (int i = 1; i <= n; ++i) {
for (auto [l, c] : A[i])
if (pos[c] < l) add(pos[c], -1), add(l, 1), pos[c] = l;
for (auto [l, id] : B[i]) ans[id] = get_sum(l);
}
for (int i = 1; i <= m; ++i) cout << ans[i] << "\n";
return 0;
}