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)∑e∈EM??value(e)<∑e∈ES??value(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\n", ans + z);
179     return 0;
180 }

后记

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

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

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

qwq

原文地址:https://www.cnblogs.com/aiyi2000/p/9832556.html

时间: 2024-10-10 00:06:51

P4180 严格次小生成树[BJWC2010]的相关文章

P4180 严格次小生成树[BJWC2010] Kruskal,倍增

题目链接\(Click\) \(Here\). 题意就是要求一个图的严格次小生成树.以前被题面吓到了没敢做,写了一下发现并不难. 既然要考虑次小我们就先考虑最小.可以感性理解到一定有一种次小生成树,可以由最小生成树删一条边再加一条边得到.我们枚举加上去的这一条边,加上去以后原\(mst\)会成为一个基环树,想让它次小就在这个环里找一条最长的边(不包含新加进去的)删掉就好.放在树上来讲,就是找到\(u\)到\(v\)路径上的最大值.这样我们就有了非严格次小生成树. 严格要怎么处理?我们需要排除新加

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

P4180 [模板]严格次小生成树[BJWC2010] 题意 题目描述 小\(C\)最近学了很多最小生成树的算法,\(Prim\)算法.\(Kurskal\)算法.消圈算法等等.正当小\(C\)洋洋得意之时,小\(P\)又来泼小\(C\)冷水了.小\(P\)说,让小\(C\)求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是\(E_M\),严格次小生成树选择的边集是\(E_S\),那么需要满足:(\(value(e)\)表示边\(e\)的权值)\

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

严格次小生成树模板 算法流程: 先用克鲁斯卡尔求最小生成树,然后给这个最小生成树树剖一下,维护边权转点权,维护最大值和严格次大值. 然后枚举没有被选入最小生成树的边,在最小生成树上查一下这条边的两端点的路径上的最长边,如果最长边等于枚举到的边的边权,那么选次长边(没有次长边的话直接跳过),然后在最小生成树的权值上减去路径上最/次长边,加上当前枚举的边的边权 因为如果加入枚举的边的,那么就形成了一个环,需要断开一条边 注意一开始单点次小值赋为0 #include<iostream> #inclu

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

传送门 次小生成树 那肯定是最小生成树上改一条边(改两条肯定不如只改其中一条小) 那就枚举所有不在最小生成树上的边 考虑如果把此边加入,另一边删除后的情况 考虑要删哪条边后才能保持树的形态,并且总长最小 加入一条边后树就会出现一个环 那么删掉的边要在加入的边连接的两点间的路径上 并且删去的边要尽量大 那就可以用LCA来求出树上两点间路径上的最长边 但是现在还有一个问题,可能删去的边和加入的边一样长 所以还要维护一下次长的边 次长边维护也不难,具体看代码 #include<iostream> #

P4180 【模板】严格次小生成树[BJWC2010](严格次小生成树)

题目链接 题意如题 做法 先做一遍最小生成树 枚举添加每一条非树边的情况,每一次构成一棵基环树,在环上找一条最长边(如果等于该非树边就用环上的严格次小边) 倍增LCA,倍增预处理的时候顺便维护严格次大值和最大值(注意细节) (如果是非严格次小生成树则只需要维护最大值即可) 代码 #include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <algo

bzoj 1977 洛谷P4180 严格次小生成树

Description: 给定一张N个节点M条边的无向图,求该图的严格次小生成树.设最小生成树边权之和为sum,那么严格次小生成树就是边权之和大于sum的最小的一个 Input: 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z. Output: 包含一行,仅一个数,表示严格次小生成树的边权和.(数据保证必定存在严格次小生成树) 思路:先求出原图的最小生成树,然后继续从小到大枚举边(x,y),对于x,y用倍

严格次小生成树[BJWC2010]

题目 维护环内最大值与严格次大值 与未放入最小生成树的边枚举加入 #include<bits/stdc++.h> #define re return #define dec(i,l,r) for(ll i=l;i>=r;--i) #define inc(i,l,r) for(ll i=l;i<=r;++i) typedef long long ll; using namespace std; template<typename T>inline void rd(T&am

【BJWC2010】次小生成树

题目链接:https://www.luogu.com.cn/problem/P4180 题目大意:给定一张含有 \(m\) 条边的无向带权图 , 求出这张图中边权之和严格大于最小生成树的次小生成树的边权之和 solution 笔者太鸽了 , 一连咕了三天 , 因此来补一下前两天的锅 这道题的思路很显然 , 先求出这张图的最小生成树 , 记它的边权之和为\(sum\) , 再考虑剩下的 \(m - n + 1\) 条边 , 如果把第 \(i\) 条, 分别连接\(a_i , b_i\), 权值为\

洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

洛谷题目传送门 %%%天平巨佬和山楠巨佬%%% 他们的题解 思路分析 具体思路都在两位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 首先kruskal把最小生成树弄出来,因为要求次小生成树.至于为什么次小一定只在最小的基础上改变了一条边,我也不会证......打表找规律大法好 剩下的可以有一堆数据结构来维护最大值和次大值(原理两位巨佬都讲清楚了,这里只分析一下算法的优劣) 倍增+LCA 山楠巨佬的做法,我也写了这一种.复杂度\(O(MlogM(kruscal)+MlogN(