P9058 [Ynoi2004] rpmtdq 题解

Description

给定一棵有边权的无根树,需要回答一些询问。

定义 dist(i,j)\texttt{dist(i,j)} 代表树上点 ii 和点 jj 之间的距离。

对于每一组询问,会给出 l,rl,r,你需要输出 min(dist(i,j))\min(\texttt{dist(i,j)}) 其中 li<jrl\leq i < j \leq rn2×105n\leq2\times 10^5q106q\leq 10^61z1091\le z\le 10^9

Solution

注意到一个点对 [l,r][l,r] 能作为询问的答案,当且仅当 min(dist(i,j))=dist(i,j)(li<jr)\min(\texttt{dist(i,j)})=\texttt{dist(i,j)}(l\leq i<j\leq r),考虑求出所有这样的 [l,r][l,r],然后二维数点即可。

先点分治,假设分治中心为 xxiixx 的距离为 aia_i

那么 dist(i,j)=ai+aj\texttt{dist(i,j)}=a_i+a_j,则如果 i<k<ji<k<jdist(i,k)dist(i,j),dist(k,j)dist(i,j)\texttt{dist(i,k)}\geq \texttt{dist(i,j)},\texttt{dist(k,j)}\geq \texttt{dist(i,j)}[i,j][i,j] 就可以加到重要区间内。

化简一下就是 max{ai,aj}mink=i+1j1ak\max\left\{a_i,a_j\right\}\leq \min_{k=i+1}^{j-1}{a_k},容易发现如果按照 aa 从小到大加点,那么加入 jj 时,ii 一定是 jj 的前驱或后继,用 set 维护即可。

时间复杂度:O(nlog2n+qlogn)O(n\log^2n+q\log n)

但是这样做常数太大了,过不了,因此点分治部分可以先按照编号大小排序,然后正反扫两边,维护单调栈即可。

Code

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 2e5 + 5, kMaxQ = 1e6 + 5;

int n, q, rt;
int mx[kMaxN], sz[kMaxN], stk[kMaxN];
int64_t dis[kMaxN], ans[kMaxQ];
bool del[kMaxN];
std::vector<int> vec;
std::vector<std::pair<int, int64_t>> qq[kMaxN];
std::vector<std::pair<int, int>> G[kMaxN], qr[kMaxN];

struct BIT {
int64_t c[kMaxN];

BIT() { memset(c, 0x3f, sizeof(c)); }

void upd(int x, int64_t v) {
for (; x; x -= x & -x) c[x] = std::min(c[x], v);
}

int64_t qry(int x) {
int64_t ret = 1e18;
for (; x <= n; x += x & -x) ret = std::min(ret, c[x]);
return ret;
}
} bit;

void getsz(int u, int fa) {
mx[u] = 0, sz[u] = 1;
for (auto [v, w] : G[u]) {
if (v == fa || del[v]) continue;
getsz(v, u);
sz[u] += sz[v], mx[u] = std::max(mx[u], sz[v]);
}
}

void getrt(int u, int fa, int tot) {
mx[u] = std::max(mx[u], tot - sz[u]);
if (mx[u] < mx[rt]) rt = u;
for (auto [v, w] : G[u]) {
if (v == fa || del[v]) continue;
getrt(v, u, tot);
}
}

void dfs2(int u, int fa) {
vec.emplace_back(u);
for (auto [v, w] : G[u]) {
if (v == fa || del[v]) continue;
dis[v] = dis[u] + w;
dfs2(v, u);
}
}

void dfs1(int u) {
dis[u] = 0, dfs2(u, 0);
std::sort(vec.begin(), vec.end());
int top = 0;
for (auto x : vec) {
for (; top && dis[stk[top]] > dis[x]; --top) {}
if (top) qq[x].emplace_back(stk[top], dis[x] + dis[stk[top]]);
stk[++top] = x;
}
top = 0;
std::reverse(vec.begin(), vec.end());
for (auto x : vec) {
for (; top && dis[stk[top]] > dis[x]; --top) {}
if (top) qq[stk[top]].emplace_back(x, dis[x] + dis[stk[top]]);
stk[++top] = x;
}
vec.clear(), vec.shrink_to_fit();
del[u] = 1;

for (auto [v, w] : G[u]) {
if (del[v]) continue;
rt = 0, getsz(v, 0), getrt(v, 0, sz[v]), dfs1(rt);
}
}

void solve() {
for (int r = 1; r <= n; ++r) {
for (auto [l, w] : qq[r]) bit.upd(l, w);
for (auto [l, id] : qr[r]) ans[id] = bit.qry(l);
}
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i < n; ++i) {
int u, v, w;
std::cin >> u >> v >> w;
G[u].emplace_back(v, w), G[v].emplace_back(u, w);
}
mx[0] = 1e9, getsz(1, 0), getrt(1, 0, n), dfs1(rt);
std::cin >> q;
for (int i = 1; i <= q; ++i) {
int l, r;
std::cin >> l >> r;
ans[i] = 1e18;
if (l != r) qr[r].emplace_back(l, i);
else ans[i] = -1;
}
solve();
for (int i = 1; i <= q; ++i) std::cout << ans[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P9058 [Ynoi2004] rpmtdq 题解
https://sobaliuziao.github.io/2024/04/05/post/279c2463.html
作者
Egg_laying_master
发布于
2024年4月5日
许可协议