题目:

a180285非常喜欢滑雪。他来到一座雪山,这里分布着M条供滑行的轨道和N个轨道之间的交点(同时也是景点),而且每个景点都有一编号i(1 ≤ i ≤ N)和一高度Hi。a180285 能从景点i 滑到景点j 当且仅当存在一条i 和j 之间的边,且i 的高度不小于j。
与其他滑雪爱好者不同,a180285喜欢用最短的滑行路径去访问尽量多的景点。如果仅仅访问一条路径上的景点,他会觉得数量太少。于是a180285拿出了他随身携带的时间胶囊。
这是一种很神奇的药物,吃下之后可以立即回到上个经过的景点(不用移动也不被认为是 a180285 滑行的距离)。
请注意,这种神奇的药物是可以连续食用的,即能够回到较长时间 之前到过的景点(比如上上个经过的景点和上上上个经过的景点)。
现在,a180285站在1号景点望着山下的目标,心潮澎湃。他十分想知道在不考虑时间 胶囊消耗的情况下,以最短滑行距离滑到尽量多的景点的方案(即满足经过景点数最大的前 提下使得滑行总距离最小)。你能帮他求出最短距离和景点数吗?


做法:

题目大意是给你一个有向图,求1所在联通块的最小树形图。
但是此题范围较大,朱刘算法行不通。
先从1出发bfs出联通块,得到1所在的有向图。
从题意中我们还得知,这个有向图不会有环,或者说不会有点数≥3的环。因为只能从高出往低处滑。但当时,u、v之间彼此可以到达。存在2元环。也可以说u,v之间是一条无向边。
那么这个问题怎么处理呢?
假如图中所有点的高度都相同。则图变成无向图了。跑最小生成树就行了,从小到大选边。而如果图有不同高度的点,我们考虑将点分层。一开始位于最高层的点必定形成一个无向图,对这些点做最小生成树。然后往下一层扩展时,将上层缩成一个点加入次高层中。此时次高层中有本层的无向边,也有上层下来的有向边。因为不存在自下往上的边,这些有向边可以当作无向边处理的。所以此时同样也是跑最小生成树。然后层层递推下去就能求出最小树形图了。
以上这个思维过程可以很简单实现。(神奇)
对边以高度为第一关键字从大到小(分层),边权为第二关键字从小到大(贪心选边)排序然后kruskal即可。


代码:

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define debug(a) cout << #a ": " << a << endl
using namespace std;
typedef long long ll;
const int N = 1e6 + 7;
const int M = 2e6 + 7;
int n, m, h[N];
struct edge{
    int u, v, w, nxt;
    bool operator<(const edge& rh)const{
        if (h[v] != h[rh.v]) return h[v] > h[rh.v];
        return w < rh.w;
    }
}e[M];
int tot, head[N];
int q[M], top, tail, vis[N]; 
int cnt, fa[N];
ll ans;
void init(void){
    tot = 0; memset(head, -1, sizeof head);
}
void addedge(int u, int v, int w){
    e[tot] = (edge){u, v, w, head[u]}; head[u] = tot++;
}
void bfs(int s){
    top = tail = cnt = 0;
    q[tail++] = s, vis[s] = 1, cnt++;
    while (top != tail){
        int u = q[top++];
        for (int i = head[u]; i != -1; i = e[i].nxt){
            int v = e[i].v;
            if (!vis[v]){
                q[tail++] = v, vis[v] = 1, cnt++;
            }
        }
    }
}
int getfa(int x){
    return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
void kruskal(void){
    ans = 0;
    for (int i = 1; i <= n; ++i) fa[i] = i;
    for (int i = 0; i < tot; ++i){
        int u = e[i].u, v = e[i].v, w = e[i].w;
        if (vis[u] && vis[v]){
            if (getfa(u) != getfa(v)){
                fa[getfa(u)] = getfa(v);
                ans += w;
            }
        }
    }
}
int main(void){
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i){
        scanf("%d", &h[i]);
    }
    init();
    for (int i = 0; i < m; ++i){
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        if (h[u] >= h[v]) addedge(u, v, w);
        if (h[v] >= h[u]) addedge(v, u, w);
    }
    bfs(1);
    sort(e,e+tot);
    kruskal();
    printf("%d %lld\n", cnt, ans);
    return 0;
}