CF2152H2 Victorious Coloring (Hard Version) 题解

Description

给定一棵有 nn 个顶点的树,每个顶点编号为 11nn。每条边都被赋予一个正整数权值 w1,w2,,wn1w_1, w_2, \ldots, w_{n-1}

一种“胜利染色”指的是将所有顶点染成红色或黄色两种颜色,其中必须至少有一个顶点染成红色(这象征着队伍 T1 的象征)。

设对每个顶点分配了一个非负整数权值 x1,x2,,xnx_1, x_2, \ldots, x_n。胜利染色的代价被定义为:所有红色顶点权值之和,加上所有连接不同颜色顶点(即红色和黄色)之间的边的权值之和。定义 f([x1,x2,,xn])f([x_1, x_2, \ldots, x_n]) 为所有胜利染色下可能的最小代价。

Gumayusi 考虑了在给定序列 x1,x2,,xnx_1, x_2, \ldots, x_n 时计算 f([x1,x2,,xn])f([x_1, x_2, \ldots, x_n]) 的问题。但对他而言这个问题太简单了,于是他改进了这个问题:给定一个整数 ll,求一组非负整数顶点权值 [x1,x2,,xn][x_1, x_2, \ldots, x_n],使得 f([x1,x2,,xn])lf([x_1, x_2, \ldots, x_n]) \ge l 且顶点权值总和 i=1nxi\sum_{i=1}^n x_i 最小。

Gumayusi 感到满意,但还存在一个严重问题——这个问题没有任何询问,对于任何不是“坏的”题目来说是不可接受的。因此,他给这个问题增加了询问。每给定一个 ll 作为询问,你需要输出相应的最小总顶点权值。

1n,q2.5×1051\leq n,q\leq 2.5\times 10^5

Solution

g(S)g(S) 表示选择 SS 点集内的点染红的边权代价和,那么问题实际上等价于选择一些互不相交的集合 S1,S2,,SkS_1,S_2,\ldots,S_k,最大化 (lg(si))\sum(l-g(s_i))

首先容易发现最优划分中 SS 一定构成一个连通块,否则随便选择任意一个连通块 TTg(T)g(T) 值一定小于 g(S)g(S),最终的结果也就更大。

还有一个结论是如果存在两条边 e1e_1e2e_2,满足 e1e_1 两个端点都在 SS 中,e2e_2 恰有一个端点在 SS 中,则一定满足 w(e1)>w(e2)w(e_1)>w(e_2)。否则把 e1e_1 断掉会把 SS 划分成两个集合 S1S_1S2S_2,选择不包含 e2e_2 的端点的那个集合,那么 gg 值一定会至少减少 w(e2)w(e1)0w(e_2)-w(e_1)\geq 0,矛盾。

注意到如果我们固定最优划分集合 SS 邻域中边权最小的边是 ee 后,只会有至多两个集合满足条件!这是因为 ee 只有两个端点,这两个集合就是两边分别走边权大于 w(e)w(e) 的边后能走到的连通块。


由于这个很类似最大生成树,所以考虑建出边权从大到小的 kruskal 重构树。那么每次选择的集合一定构成一个子树。

每个子树的 gg 值容易树上差分求,设 sus_u 表示 uu 选择子树的 gg 值,fuf_u 表示 uu 子树能选择的最大 (ksu)\sum(k-s_u)

容易得到转移:fumax(flsu+frsu,ksu)f_u\leftarrow \max(f_{ls_u}+f_{rs_u},k-s_u),直接转移是 O(nq)O(nq) 的。

优化就考虑最终的答案一定是由 ki+wk\cdot i+w 的形式构成的,设 gu,ig_{u,i} 表示 uu 的子树中系数为 ii 的最大 ww 值。

转移变为 gu,i+jglsu,i+grsu,j,gu,1sug_{u,i+j}\leftarrow g_{ls_u,i}+g_{rs_u,j},g_{u,1}\leftarrow -s_u,很遗憾这个东西不是凸的,但是由于我们只需要求 ki+gu,ik\cdot i+g_{u,i} 的最大值,所以维护出凸包然后闵可夫斯基和合并即可,gu,1g_{u,1} 的转移需要单独处理一下。

时间复杂度: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
122
123
124
125
126
127
128
129
130
131
#include <bits/stdc++.h>

#define int int64_t

template<class T>
struct Vector {
T x, y;

Vector() {}
Vector(T _x, T _y) : x(_x), y(_y) {}

friend bool operator ==(Vector a, Vector b) { return a.x == b.x && a.y == b.y; }
friend bool operator !=(Vector a, Vector b) { return a.x != b.x || a.y != b.y; }
friend Vector operator -(Vector a) { return {-a.x, -a.y}; }
friend Vector operator +(Vector a, Vector b) { return {a.x + b.x, a.y + b.y}; }
friend Vector operator -(Vector a, Vector b) { return {a.x - b.x, a.y - b.y}; }
friend T operator *(Vector a, Vector b) { return a.x * b.y - a.y * b.x; }
template<class _T> friend Vector operator *(Vector a, _T b) { return {a.x * b, a.y * b}; }
template<class _T> friend Vector operator *(_T a, Vector b) { return {a * b.x, a * b.y}; }
template<class _T> friend Vector operator /(Vector a, _T b) { return {a.x * 1.0 / b, a.y * 1.0 / b}; }
friend Vector operator +=(Vector &a, Vector b) { return a = {a.x + b.x, a.y + b.y}; }
friend Vector operator -=(Vector &a, Vector b) { return a = {a.x - b.x, a.y - b.y}; }
template<class _T> friend Vector operator *=(Vector &a, _T b) { return a = {a.x * b, a.y * b}; }
template<class _T> friend Vector operator /=(Vector &a, _T b) { return a = {a.x * 1.0 / b, a.y * 1.0 / b}; }
friend bool operator <(Vector a, Vector b) { return a * b > 0; }
};

using i64 = int64_t;
using Vec = Vector<i64>;

const int kMaxN = 5e5 + 5;

int n, q, cnt, k, m;
int u[kMaxN], v[kMaxN], w[kMaxN], fa[kMaxN], id[kMaxN], s[kMaxN];
int ls[kMaxN], rs[kMaxN], f[kMaxN], g[kMaxN];
std::priority_queue<Vec> qq[kMaxN];

inline void chkmax(int &x, int y) { x = (x > y ? x : y); }
inline void chkmin(int &x, int y) { x = (x < y ? x : y); }

int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}

void unionn(int x, int y, int w) {
int fx = find(x), fy = find(y);
if (fx != fy) {
++cnt, ls[cnt] = id[fx], rs[cnt] = id[fy], s[cnt] = s[id[fx]] + s[id[fy]] - 2 * w;
fa[fx] = fy, id[fy] = cnt;
}
}

void build() {
cnt = n;
for (int i = 1; i <= n; ++i) fa[i] = id[i] = i;
std::vector<std::tuple<int, int, int>> ed;
for (int i = 1; i < n; ++i) ed.emplace_back(w[i], u[i], v[i]);
std::sort(ed.begin(), ed.end(), std::greater<>());
for (auto [w, u, v] : ed) {
unionn(u, v, w);
}
}

void dfs(int u) {
for (; qq[u].size(); qq[u].pop()) {}
if (u <= n) {
qq[u].emplace(1, -s[u]);
} else {
dfs(ls[u]), dfs(rs[u]);
if (qq[ls[u]].size() < qq[rs[u]].size()) qq[ls[u]].swap(qq[rs[u]]);
qq[u].swap(qq[ls[u]]);
for (; qq[rs[u]].size(); qq[rs[u]].pop()) qq[u].emplace(qq[rs[u]].top());
Vec p = {1, -s[u]};
if (qq[u].top() < p) {
for (; qq[u].size() >= 2;) {
auto p1 = qq[u].top(); qq[u].pop();
auto p2 = qq[u].top();
if ((p1 - p) * p2 >= 0) qq[u].pop(), qq[u].emplace(p1 + p2);
else { qq[u].emplace(p1); break;}
}
if (qq[u].size()) {
auto pp = qq[u].top(); qq[u].pop();
if ((pp - p).x > 0) qq[u].emplace(pp - p);
}
qq[u].emplace(p);
}
}
}

int solve(int k) {
int L = 0, R = m + 1, res = 0;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (k * f[mid] + g[mid] > k * f[mid - 1] + g[mid - 1]) L = res = mid;
else R = mid;
}
return k * f[res] + g[res];
}

void dickdreamer() {
std::cin >> n;
std::fill_n(s + 1, n, 0);
for (int i = 1; i < n; ++i)
std::cin >> u[i] >> v[i] >> w[i], s[u[i]] += w[i], s[v[i]] += w[i];
build();
dfs(cnt);
for (m = 0; qq[cnt].size();) {
++m;
auto [x, y] = qq[cnt].top(); qq[cnt].pop();
f[m] = f[m - 1] + x, g[m] = g[m - 1] + y;
}
std::cin >> q;
for (int i = 1; i <= q; ++i) {
int k;
std::cin >> k;
std::cout << solve(k) << '\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;
}

CF2152H2 Victorious Coloring (Hard Version) 题解
https://sobaliuziao.github.io/2025/10/07/post/bf9a3cef.html
作者
Egg_laying_master
发布于
2025年10月7日
许可协议