Luogu 2573 [SCOI2012]滑雪

BZOJ 2753

首先可以按照题目要求的把所有的有向边建出来,然后进去广搜就可以求出第一问的解,然后考虑如何求解第二问,我们把所有搜到的边按照到达的点的高度位第一关键字,边的长度为第二关键字排序之后跑$kruskal$,这样子得到的最小生成树权值就是第二问所求的最大值。

考虑一下这样子为什么正确,首先“时间胶囊”的返回形式让答案一定是一棵生成树的权值,但是直接跑最小生成树可能会造成有一些点不能走到的情况,所以我们搜出所有的可能能到达的点,然后按照点的海拔高度先排序之后就一定能保证所有点都能走到,这样子求出来的生成树也一定是最小代价的。

时间复杂度$O(mlogm)$。

Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;

const int N = 1e5 + 5;
const int M = 1e6 + 5;

int n, m, num = 0, tot = 0, head[N], edgeNum = 0, ufs[N];
ll h[N];
bool vis[N];

struct Edge {
    int to, nxt;
    ll val;
} e[M << 1];

inline void add(int from, int to, ll val) {
    e[++tot].to = to;
    e[tot].val = val;
    e[tot].nxt = head[from];
    head[from] = tot;
}

struct Pathway {
    int u, v;
    ll val;
    
    friend bool operator < (const Pathway &x, const Pathway &y) {
        if(h[x.v] != h[y.v]) return h[x.v] > h[y.v];
        else return x.val < y.val;
    }
    
} pat[M << 1];

template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for(; ch > '9'|| ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

inline void init() {
    for(int i = 1; i <= n; i++) ufs[i] = i;
}

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

queue <int> Q;
void bfs() {
    Q.push(1); 
    vis[1] = 1; num = 1;
    for(; !Q.empty(); ) {
        int x = Q.front(); Q.pop();
        for(int i = head[x]; i; i = e[i].nxt) {
            int y = e[i].to;
            pat[++edgeNum] = (Pathway) {x, y, e[i].val};
            if(!vis[y]) {
                vis[y] = 1; ++num;
                Q.push(y);
            }
        }
    }
}

inline ll kruskal() {
    init();
    sort(pat + 1, pat + 1 + edgeNum);
    ll res = 0LL;
    for(int cnt = 0, i = 1; i <= edgeNum; i++) {
        int u = find(pat[i].u), v = find(pat[i].v);
        if(u == v) continue;
        ufs[u] = v;
        ++cnt;
        res += pat[i].val;
        if(cnt >= num - 1) break;
    }
    return res;
}

int main() {
//    freopen("3.in", "r", stdin);
    
    read(n), read(m);
    for(int i = 1; i <= n; i++) read(h[i]);
    for(int i = 1; i <= m; i++) {
        int x, y; ll v;
        read(x), read(y), read(v);
        if(h[x] >= h[y]) add(x, y, v);
        if(h[x] <= h[y]) add(y, x, v);
    }
    
    bfs();
    
    printf("%d %lld\n", num, kruskal());
    return 0;
}
View Code

 

posted @ 2018-10-22 20:15  CzxingcHen  阅读(115)  评论(0编辑  收藏  举报