题目链接:https://codeforces.com/contest/1120/problem/D
3.1 题意
给你一棵有 n ( 2 ≤ n ≤ 2 ⋅ 1 0 5 ) n(2 \le n \le 2 \cdot 10^5) n(2≤n≤2⋅105) 个节点的有根树,根为 1 1 1。每个节点 i i i 都有一个价格 c i ( 0 ≤ c i ≤ 1 0 9 ) c_i(0 \le c_i \le 10^9) ci(0≤ci≤109)。
Arkady
和 Vasily
在树上进行一个游戏,其步骤如下:
Arkady
购买一些点。Vasily
会在所有的叶子上面放置数字。Arkady
会进行若干次(可以为 0 0 0 次)如下操作:选择一个其购买的节点 v v v,然后将 v v v 的子树中的所有叶子节点的值加上 x x x( x x x 可为任意整数)。
Arkady
的目标是将所有的叶子节点的值变为
0
0
0,问其最小的花费。
之后输出所有的在至少一个最优方案中出现的节点。
3.2 解题过程
可以发现,每一个节点 v v v 可以控制的叶子节点是一段区间 [ a , b ] [a,b] [a,b],因此对节点 v v v 进行操作等价于对区间 [ a , b ] [a,b] [a,b] 进行差分。
如果对于每一个点 v v v 及其所对应的区间 [ a , b ] [a,b] [a,b],都建边 ( a , b + 1 , c v ) (a,b+1,c_v) (a,b+1,cv) 的话,我们就可以通过跑一遍最小生成树来得到最优解。
最小生成树表明了所有的点是连通的,也就是说每个点的权值都可以被自由更新,而最小生成树中的权值和最小也就是花费最小。
而至少一个最优方案中出现的节点就是在至少一棵最小生成树中的边。
可以参考 Codeforces-160D
中的类似做法来求出这一类边。
时间复杂度: O ( n log n ) O(n \log n) O(nlogn)。
另外本题也有树形 dp 的做法。
3.3 代码
int head[maxn], cnt, n, tot, mstnum;
int dfn[maxn], father[maxn], rank_[maxn], sz[maxn];
pii range[maxn];
ll c[maxn];
bool mark[maxn];
set<int> se;
unordered_map<ll, int> mp;
struct node {
int v, nxt;
} Edge[2 * maxn];
struct edge {
int u, v, id;
ll w;
edge() {}
edge(int _id, int _u, int _v, ll _w):
id(_id), u(_u), v(_v), w(_w) {}
} mst[2 * maxn];
bool operator < (edge a, edge b) {
return a.w < b.w;
}
vector<edge> ve[maxn];
void init() {
for (int i = 0; i <= n; i++) {
head[i] = -1;
father[i] = i;
rank_[i] = 0;
}
cnt = 0;
tot = 0;
mstnum = 0;
}
void addedge(int u, int v) {
Edge[cnt].v = v;
Edge[cnt].nxt = head[u];
head[u] = cnt++;
}
void dfs(int id, int fa) {
sz[id] = 1;
range[id].first = 0x3f3f3f3f;
range[id].second = -1;
for (int i = head[id]; i != -1; i = Edge[i].nxt) {
int v = Edge[i].v;
if (v == fa) continue;
dfs(v, id);
range[id].first = min(range[id].first, range[v].first);
range[id].second = max(range[id].second, range[v].second);
sz[id] += sz[v];
}
if (sz[id] == 1) {
dfn[id] = ++tot;
range[id].first = range[id].second = tot;
}
}
int find(int id) {
return father[id] == id ? id : father[id] = find(father[id]);
}
void merge(int x, int y) {
x = find(x);
y = find(y);
if (x != y) {
if (rank_[x] > rank_[y]) father[y] = x;
else {
father[x] = y;
if (rank_[x] == rank_[y]) rank_[y]++;
}
}
}
int main()
{
int u, v;
scanf("%d", &n);
init();
for (int i = 1; i <= n; i++) {
scanf("%lld", &c[i]);
}
for (int i = 1; i < n; i++) {
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
dfs(1, -1);
ll ans = 0;
int num = 0;
for (int i = 1; i <= n; i++) {
mst[i] = edge(i, range[i].first, range[i].second + 1, c[i]);
}
sort(mst + 1, mst + 1 + n);
for (int i = 1; i <= n; i++) {
if (mp.find(mst[i].w) == mp.end()) {
mp[mst[i].w] = ++num;
}
ve[mp[mst[i].w]].pb(mst[i]);
}
for (int i = 1; i <= num; i++) {
for (auto ed: ve[i]) {
int x = find(ed.u);
int y = find(ed.v);
int id = ed.id;
if (x != y) {
se.insert(id);
}
}
for (auto ed: ve[i]) {
int x = find(ed.u);
int y = find(ed.v);
if (x != y) {
ans += ed.w;
merge(x, y);
}
}
}
printf("%lld %d\n", ans, (int)se.size());
for (auto x: se) {
printf("%d ", x);
}
return 0;
}