QOJ #5421. Factories Once More 题解

Description

有一片由 nn 座城市组成的王国。城市的编号从 11nn(含两端)且有 (n1)(n − 1) 条道路连接各个城市。对于任意两座城市,居民们都可以沿着这些道路互相访问。

皇后最近决定建设 kk 座新的工厂。为了防止污染,她规定每座城市最多只能建立一座工厂。

您作为皇家设计师,需要在规划建设的同时,求出两两工厂之间距离之和的最大值。

两座工厂之间的距离,即为两座工厂所在的两座城市之间的最短路径长度。路径的长度即为路径中所有边的长度之和。

n105n\leq 10^5

Solution

对于每条边算贡献,假设一条边下面那个点的子树里选了 cc 个点,那么贡献为 c(kc)c(k-c),所以考虑对于子树树从下往上 dp。

fu,if_{u,i} 表示 uu 的子树里选了 ii 个点,uu 子树里的边(包括 uu 与其父亲的边)的最大总贡献。

那么首先有转移:fu,i+jfu,i+fv,jf_{u,i+j}\leftarrow f'_{u,i}+f_{v,j}

得到子树后的贡献后,再加上 uu 父亲上的边的贡献,即:fu,ifu,i+wi(ki)f_{u,i}\leftarrow f_{u,i}+wi(k-i)

但是这里有个 (max,+)(\max,+) 卷积,不能暴力转移。注意到 wi(ki)wi(k-i) 是下凸的,其差分为 w(k2i+1)w(k-2i+1),所以 dp 数组也是下凸的。用平衡树维护差分数组,需要支持启发式合并以及加等差数列,直接打标记即可。

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

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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 1e5 + 5;

int n, k;
int rt[kMaxN];
std::vector<std::pair<int, int>> G[kMaxN];
std::mt19937 rnd(114514);

struct FHQTreap {
int tot, ls[kMaxN], rs[kMaxN], sz[kMaxN], val[kMaxN], rd[kMaxN], tag1[kMaxN], tag2[kMaxN];
int newnode(int v) {
sz[++tot] = 1, val[tot] = v, ls[tot] = rs[tot] = tag1[tot] = tag2[tot] = 0, rd[tot] = rnd();
return tot;
}
void pushup(int x) {
sz[x] = sz[ls[x]] + sz[rs[x]] + 1;
}
void addtag1(int x, int v) { val[x] += v, tag1[x] += v; }
void addtag2(int x, int v) {
val[x] += v * (sz[ls[x]] + 1), tag2[x] += v;
}
void pushdown(int x) {
if (tag1[x]) {
if (ls[x]) addtag1(ls[x], tag1[x]);
if (rs[x]) addtag1(rs[x], tag1[x]);
tag1[x] = 0;
}
if (tag2[x]) {
if (ls[x]) addtag2(ls[x], tag2[x]);
if (rs[x]) addtag1(rs[x], tag2[x] * (sz[ls[x]] + 1)), addtag2(rs[x], tag2[x]);
tag2[x] = 0;
}
}
int merge(int x, int y) {
// std::cerr << "??? " << x << ' ' << y << ' ' << ls[x] << ' ' << ls[y] << ' ' << rs[x] << ' ' << rs[y] << '\n';
if (!x || !y) return x + y;
pushdown(x), pushdown(y);
if (rd[x] < rd[y]) {
rs[x] = merge(rs[x], y), pushup(x);
return x;
} else {
ls[y] = merge(x, ls[y]), pushup(y);
return y;
}
}
void split(int x, int v, int &a, int &b) {
if (!x) return void(a = b = 0);
pushdown(x);
if (val[x] >= v) {
a = x, split(rs[x], v, rs[x], b);
pushup(a);
} else {
b = x, split(ls[x], v, a, ls[x]);
pushup(b);
}
}
void ins(int &rt, int x) {
int a, b;
// std::cerr << "??? " << rt << ' ' << x << '\n';
split(rt, val[x], a, b);
rt = merge(a, merge(x, b));
}
void update(int rt, int v1, int v2) {
// std::cerr << "fuck " << rt << ' ' << v1 << ' ' << v2 << '\n';
addtag1(rt, v1), addtag2(rt, v2);
}
void insall(int &x, int y) {
if (sz[x] < sz[y]) std::swap(x, y);
// std::cerr << sz[x] << ' ' << sz[y] << ' ';
std::vector<int> id;
std::function<void(int)> dfs = [&] (int x) {
if (!x) return;
pushdown(x);
if (ls[x]) dfs(ls[x]);
if (rs[x]) dfs(rs[x]);
ls[x] = rs[x] = tag1[x] = tag2[x] = 0, sz[x] = 1;
id.emplace_back(x);
};
dfs(y);
for (auto i : id) ins(x, i);
// std::cerr << id.size() << ' ' << sz[x] << '\n';
}
void print(int x) {
if (!x) return;
pushdown(x);
if (ls[x]) print(ls[x]);
std::cerr << val[x] << ' ';
if (rs[x]) print(rs[x]);
}
int getsum(int x, int k) {
if (!x) return 0;
// std::cerr << "??? " << x << ' ' << val[x] << ' ' << k << ' ' << sz[ls[x]] << '\n';
pushdown(x);
assert(sz[x] >= k);
if (k <= sz[ls[x]]) return getsum(ls[x], k);
else if (k <= sz[ls[x]] + 1) return getsum(ls[x], sz[ls[x]]) + val[x];
else return getsum(ls[x], sz[ls[x]]) + val[x] + getsum(rs[x], k - sz[ls[x]] - 1);
}
} t;

void dfs(int u, int fa, int faw) {
rt[u] = t.newnode(0);
// std::cerr << t.sz[rt[u]] << '\n';
for (auto [v, w] : G[u]) {
if (v == fa) continue;
dfs(v, u, w);
t.insall(rt[u], rt[v]);
}
t.update(rt[u], faw * (k + 1), -2 * faw);
// t.print(rt[u]), std::cerr << '\n';
}

void dickdreamer() {
std::cin >> n >> k;
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);
}
dfs(1, 0, 0);
// t.print(rt[1]), std::cerr << '\n';
std::cout << t.getsum(rt[1], k) << '\n';
// std::cerr << t.sz[rt[1]] << '\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;
}

QOJ #5421. Factories Once More 题解
https://sobaliuziao.github.io/2025/09/28/post/b16c7aba.html
作者
Egg_laying_master
发布于
2025年9月28日
许可协议