• P4180 严格次小生成树[BJWC2010]


    题目链接

    https://www.luogu.org/problemnew/show/P4180

    题目描述

    小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) sum_{e in E_M}value(e)<sum_{e in E_S}value(e)eEMvalue(e)<eESvalue(e)

    这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

    输入输出格式

    输入格式:

    第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

    输出格式:

    包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

    输入输出样例

    输入样例#1: 
    5 6
    1 2 1 
    1 3 2 
    2 4 3 
    3 5 4 
    3 4 3 
    4 5 6 
    输出样例#1: 
    11

    说明

    数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。 

    题目分析

    所谓严格的次小是指权值严格大于最小生成树的次小生成树,我们知道一般次小生成树,只需要先用kruskal算法求得最小生成树,然后暴力枚举非树边,替换路径最大边即可。

    这题也可以类似思考,但有一个问题,如果最大边与当前枚举边相等时,我们不能替换,于是求其次用次小边来替换。这样我们需要求得路径上的最小边和次小边(小于最小边),于是我们可以利用LCA的倍增算法来维护。

    预处理过程需要考虑i -> f[i][j]与f[i][j] -> f[f[i][j]][j]这两段的合并,考虑这两段的最大值相同与不同情况,相同则说明次大值是这两个的次大值的最大值,不同的话,假设(a,b),(c,d)表示两段的(最大,次大),若a > c,显然次大为max(b, c), c > a的情况类似,见代码中的函数ck1。

    预处理完,维护沿单链向上跳,记单链的(最大,次大)为(a, b),当前得到最优值(lx, ln),分三种情况讨论,lx与a的大小关系,见代码中的函数ck3。

    参考代码

      1 #include <cstdio>
      2 #include <iostream>
      3 #include <cstring>
      4 #include <algorithm>
      5 
      6 using namespace std;
      7 //#define DEBUG(x) cerr << #x << "=" << x << endl
      8 #define Maxn 300010
      9  
     10 struct edge
     11 {
     12     int to;
     13     int w;
     14     int next;
     15 }p[Maxn];
     16 
     17 int head[Maxn / 3], tot;
     18 
     19 void addedge(int a, int b, int c)
     20 {
     21     p[tot].to = b;
     22     p[tot].w = c;
     23     p[tot].next = head[a];
     24     head[a] = tot++;
     25 }
     26 
     27 struct line
     28 {
     29     int u, v, w;
     30     bool operator<(const line &a)const
     31     {
     32         return w < a.w;
     33     }
     34 }q[Maxn];
     35  
     36 int vis[Maxn];
     37 int fa[Maxn / 3];
     38 
     39 int findset(int x)
     40 {
     41     return fa[x] == x ? x : (fa[x] = findset(fa[x]));
     42 }
     43 
     44 int unionset(int a, int b)
     45 {
     46     return fa[findset(a)] = findset(b);
     47 }
     48 int dep[Maxn / 3];
     49 int f[Maxn / 3][20], g[Maxn / 3][20], h[Maxn / 3][20];
     50 
     51 void dfs(int u, int fa)
     52 {
     53     f[u][0] = fa;
     54     dep[u] = dep[fa] + 1;
     55     for (int i = head[u]; i != -1; i = p[i].next)
     56     {
     57         int v = p[i].to;
     58         if(v != fa)
     59         {
     60             g[v][0] = p[i].w;
     61             h[v][0] = -1;
     62             dfs(v, u);
     63         }
     64     }
     65 }
     66 
     67 void ck1(int &a, int &b, int c, int d, int e, int f)
     68 {
     69     if (c == e)
     70     {
     71         a = c;
     72         b = max(d, f);
     73         return;
     74     }
     75     if (c > e)
     76     {
     77         swap(c, e);
     78         swap(d, f);
     79     } 
     80     a = e;
     81     b = max(c, f);
     82 }
     83 
     84 int ck2(int lx, int ln, int w)
     85 {
     86     if (w == lx) return w-ln; 
     87     return w-lx; 
     88 }
     89 
     90 void ck3(int &lx, int &ln, int u, int t)
     91 {
     92     if (g[u][t] == lx) 
     93         ln = max(ln, h[u][t]);
     94     else if(g[u][t] < lx) 
     95         ln = max(ln, g[u][t]);
     96     else
     97     {
     98         ln = (lx, h[u][t]);
     99         lx = g[u][t];
    100     }
    101 }
    102 void init(int n)
    103 {
    104     dfs(1, 0);
    105     for (int j = 0; j < 18; j++)
    106         for (int i = 1; i <= n; i++)
    107         {
    108             if (!f[i][j]) f[i][j + 1] = 0;
    109             else
    110             {
    111                 f[i][j + 1] = f[f[i][j]][j];
    112                 ck1(g[i][j+1], h[i][j+1], g[i][j], h[i][j], g[f[i][j]][j], h[f[i][j]][j]);
    113             }
    114         }
    115 }
    116 
    117 int LCA(int u, int v, int w)
    118 {
    119     int lx = -1, ln = -1;
    120     if (dep[u] < dep[v]) swap(u,v);
    121     int df = dep[u] - dep[v], t = 0;
    122     while(df)
    123     {
    124         if(df&1)
    125         {
    126             ck3(lx, ln, u, t);
    127             u = f[u][t];
    128         }
    129         t++;
    130         df>>=1;
    131     }
    132     if(u == v) return ck2(lx, ln, w);
    133     for(int i = 18; i >= 0; i--)
    134     {
    135         if(f[u][i] != f[v][i])
    136         {
    137             ck3(lx, ln, u, i);
    138             ck3(lx, ln, v, i);
    139             u=f[u][i];
    140             v=f[v][i];
    141         }
    142     }
    143     ck3(lx, ln, u, 0);
    144     ck3(lx, ln, v, 0);
    145     return ck2(lx, ln, w);
    146 }
    147 int main()
    148 {
    149     freopen("tree.in", "r", stdin);
    150     freopen("tree.out", "w", stdout);
    151     int n, m;
    152     scanf("%d%d", &n, &m);
    153     for (int i = 0; i < m; i++)
    154         scanf("%d%d%d", &q[i].u, &q[i].v, &q[i].w);
    155     sort(q, q + m);
    156     for (int i = 1; i <= n; i++) fa[i] = i;
    157     memset(head, -1, sizeof(head));
    158     memset(vis, 0, sizeof(vis));
    159     tot = 0;
    160     int cnt = 0;
    161     long long ans = 0;
    162     for (int i = 0; i < m; i++)
    163     {
    164         int u = q[i].u, v = q[i].v;
    165         if (findset(u) == findset(v)) continue;
    166         unionset(u, v);
    167         vis[i] = 1;
    168         addedge(u, v, q[i].w);
    169         addedge(v, u, q[i].w);
    170         ans += q[i].w;
    171         if (++cnt==n-1) break;
    172     }
    173     init(n);
    174     int z = 0x3f3f3f3f;
    175     for (int i = 0; i < m; i++)
    176         if (!vis[i]) 
    177             z = min(z, LCA(q[i].u, q[i].v, q[i].w));
    178     printf("%lld
    ", ans + z);
    179     return 0;
    180 }

    后记

    本来想着在这篇博客的后面讲解一下倍增LCA和求最小生成树的两种方法

    但是想了想,这样的话未免太乱了些

    于是决定再开新博客来讲解

    qwq

  • 相关阅读:
    3.04
    3.03
    3.02
    3.01
    2.27
    2.25小账本6
    2.24小账本5
    2.23小账本4
    2.22小账本3
    git常用命令
  • 原文地址:https://www.cnblogs.com/aiyi2000/p/9832556.html
Copyright © 2020-2023  润新知