抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

题目传送门

题意

给定一个\(n\)个点,\(m\)条边的有权无向图,每个点有一个高度,求一个以\(1\)为根的树,满足父亲的高度大于等于儿子的高度。在树的节点数最多的情况下,树上的边权之和最小。

题解

如果没有高度限制,直接\(Kruskal\)一遍就好了。

有了高度呢?其实变成了有向图,求一个点最多,边权值和最小的生成树。

第一问很好做,直接对这个有向图从\(1\)开始\(bfs/dfs\)一遍就好了。

\(bfs/dfs\)之后,原来的有向图就变成了从1开始能到达的所有点组成的一个有向图。

显然,如果两个点高度相同,那么这两个点之间连的边可以看成无向边,直接按边权排序做即可。

再考虑一个问题,两个点\(x,y\)需要在树上联通,一定不会通过另一个高度比它们小的点进行联通的。

所以我们对于新图中的每条边\(u\to v\),优先按\(h_v\)从大到小,\(h_v\)相等按边权从小到大排序即可。

然后就是最简单的Kruskal了。

代码

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
/**************************************************************
Problem: 2753
User: rill7747
Language: C++
Result: Accepted
Time:6972 ms
Memory:84612 kb
****************************************************************/

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
int read(){
register int x = 0;
register char f = 1, ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = 0;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
return f ? x : -x;
}
#define N 2000005
int n, m, h[N], fa[N], cnt;
int edge, to[N], tw[N], pr[N], hd[N], vis[N];
void addedge(int u, int v, int w){
to[++edge] = v, tw[edge] = w, pr[edge] = hd[u], hd[u] = edge;
}
struct node{
int u, v, w;
bool operator < (const node &res) const {
return h[v] > h[res.v] || h[v] == h[res.v] && w < res.w;
}
}E[N];
void dfs(int u){
++cnt, vis[u] = 1;
for (register int i = hd[u], v; i; i = pr[i]){
if (!vis[v = to[i]]) dfs(v);
E[++m] = (node){u, v, tw[i]};
}
}
int find(int x){
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
int merge(int x, int y){
int fx = find(x), fy = find(y);
if (fx == fy) return 0;
return fa[fy] = fx, 1;
}
long long Kruskal(){
std :: sort(E + 1, E + 1 + m);
for (register int i = 1; i <= n; ++i) fa[i] = i;
long long ans = 0;
int sum = 0;
for (register int i = 1; i <= m; ++i){
if (merge(E[i].u, E[i].v)) ans += E[i].w, ++sum;
if (sum == cnt - 1) break;
}
return ans;
}
int main(){
n = read(), m = read();
for (register int i = 1; i <= n; ++i) h[i] = read();
for (register int i = 1; i <= m; ++i){
int u = read(), v = read(), w = read();
if (h[u] >= h[v]) addedge(u, v, w);
if (h[v] >= h[u]) addedge(v, u, w);
}
m = 0, dfs(1);
printf("%d %lld", cnt, Kruskal());
}

评论