洛谷 P4180 【模板】严格次小生成树[BJWC2010]

版权声明:转载请说明,欢迎交流! https://blog.csdn.net/qq_39599067/article/details/82936940

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

Catalog

Problem:Portal传送门

 原题目描述在最下面。

 意思很裸,就是求一个严格意义的次小生成树,树边权和要小于 M S T MST 边权和。


Solution:

 本来以为就普通的次小生成树,然后取不等于 M S T MST 的即可,结果样例都过不了!

 ( p s ps : 如果你不会求普通次小生成树,可能看不懂下面这段话,唉)

 后来发现这样是错的,这种方法能保证求出非严格次小生成树,仅此而已!其他权值都是无意义的。因为普通方法使用 M S T MST u v u-v 路径上最大的边权和 u v u-v 交换,若两者边权相等,这样的答案是不满足要求的,最重要的是你漏解了!

Kruskal+倍增:

 这是一种比较好写的正解。

 为解决上面的问题,用倍增来维护 M S T MST 上每个点往上的最大边权 m x 1 mx1 和严格次大边权 m x 2 mx2

 枚举不在 M S T MST 中的边,求 u , v u,v L C A LCA c f = L C A cf =LCA 。再求出 c f > u cf->u c f > v cf->v 上的不等于 w w 的最大边权。

 若 m x 1 = = w mx1 == w , 则用 m x 2 mx2 交换 w w ;反之用 m x 1 mx1 交换 w w

 ( u , v , w u,v,w 是不在生成树中的边的端点和权值)

 具体细节看代码吧,不懂的可以评论区留言。


AC_Code:

/*
5 6
1 2 1 
1 3 2 
2 4 3 
3 5 4 
3 4 3 
4 5 6 
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <ctime>
#include<iostream>
using namespace std;
typedef long long LL;

const int MXN = 1e5 + 5;
const int MXE = 6e5 + 5;
const int INF = 0x3f3f3f3f;
int n, m;
LL dis[MXN], dis1[MXN][23], dis2[MXN][23];
int dep[MXN], up[MXN][23];
int FA[MXN], rk[MXN];
int head[MXN], tot;
struct lp{
    int v, w, nex;
}cw[MXE];
struct EDGE{
    int u, v, w, is;
}edge[MXE];
bool cmp(const EDGE& a, const EDGE& b) {
    return a.w < b.w;
}
void init() {
    tot = -1;
    memset(up, 0, sizeof(up));
    memset(head, -1, sizeof(head));
    memset(dis, 0, sizeof(dis));
    memset(dis1, -1, sizeof(dis1));
    memset(dis2, 0, sizeof(dis2));
    memset(dep, 0, sizeof(dep));
}
void add(int u,int v,int w) {
    cw[++tot].v = v; cw[tot].nex = head[u]; cw[tot].w = w;
    head[u] = tot;
    cw[++tot].v = u; cw[tot].nex = head[v]; cw[tot].w = w;
    head[v] = tot;
}
void dfs(int u, int ba, int d) {
    dep[u] = d; up[u][0] = ba;
    for(int i = 1; i < 20; ++i) {
        int cf = up[u][i-1];
        up[u][i] = up[cf][i-1];
        dis1[u][i] = max(dis1[u][i-1], dis1[cf][i-1]);
        if(dis1[u][i-1] == dis1[cf][i-1]) dis2[u][i] = max(dis2[u][i-1], dis2[cf][i-1]);
        else {
            dis2[u][i] = min(dis1[u][i-1], dis1[cf][i-1]);
            dis2[u][i] = max(dis2[u][i], max(dis2[u][i-1], dis2[cf][i-1]));
        }
    }
    for(int i = head[u]; ~i; i = cw[i].nex) {
        int v = cw[i].v;
        if(v == ba) continue;
        dis[v] = dis[u] + cw[i].w;
        dis1[v][0] = cw[i].w;
        up[v][0] = u;
        dfs(v, u, d + 1);
    }
}
int LCA(int x, int y) {
    if(dep[x] < dep[y]) swap(x, y);
    for(int i = 19; i >= 0; --i) {
        if(dep[up[x][i]] >= dep[y]) {
            x = up[x][i];
        }
    }
    if(x == y) return x;
    for(int i = 19; i >= 0; --i) {
        if(up[x][i] != up[y][i]) {
            x = up[x][i]; y = up[y][i];
        }
    }
    return up[x][0];
}
int Fi(int x) {
    return FA[x] == x? x: FA[x] = Fi(FA[x]);
}
LL calc(int u, int v,int w) {
    if(dep[u] < dep[v]) swap(u, v);
    int k = dep[u] - dep[v];
    LL mx1 = 0, mx2 = 0;
    for(int i = 0; i < 20; ++i) {
        if((1<<i)&k) {
            if(dis1[u][i] > mx1) {
                mx2 = mx1;
                mx1 = dis1[u][i];
            }
            mx2 = max(mx2, dis2[u][i]);
            u = up[u][i];
        }
    }
    if(mx1 != w) return w - mx1;
    return w - mx2;
}
void solve() {
    scanf("%d%d", &n, &m);
    init();
    for(int i = 0; i < m; ++i) {
        scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
        edge[i].is = 0;
    }
    for(int i = 1; i <= n; ++i) FA[i] = i, rk[i] = 1;
    sort(edge, edge + m, cmp);
    int cnt = 0;
    LL MAX = 0;
    for(int i = 0; i < m; ++i) {
        int pa = Fi(edge[i].u), pb = Fi(edge[i].v);
        if(pa == pb) continue;
        if(rk[pa] > rk[pb]) swap(pa,pb);
        FA[pa] = pb;
        rk[pb] += rk[pa];
        add(edge[i].u, edge[i].v,edge[i].w);
        MAX += edge[i].w;
        cnt ++;
        edge[i].is = 1;
        if(cnt == n-1) break;
    }
    //printf("*** %d\n", MAX);
    up[1][0] = 1;
    dis1[1][0] = 0;
    dfs(1, 1, 1);
    LL tmp = 1e18;
    for(int i = 0; i < m; ++i) {
        if(edge[i].is) continue;
        int cf = LCA(edge[i].u,edge[i].v);
        tmp = min(tmp, calc(edge[i].u,cf,edge[i].w));
        tmp = min(tmp, calc(edge[i].v,cf,edge[i].w));
        //printf("tmp = %d\n", tmp);
    }
    printf("%lld\n", MAX + tmp);
}
int main(int argc, char const *argv[]){
    solve();
    return 0;
}

Problem Description:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_39599067/article/details/82936940