<h1>题目描述</h1>
给定一张图,求出它的严格次小生成树。
<h1>解题报告</h1>
我们首先来考虑一下严格最小生成树和不严格次小生成树的区别。
首先,对于一个图,可能不只有一个最小生成树。
那么,不严格次小生成树,只需要找出不同于该最小生成树中的生成树中权值最小的那一个,同理,这也有可能有多个。
对于严格次小生成树,必须权值要比最小生成树的权值大。当然也有可能有多个。
先考虑不严格次小生成树怎么做。
<h2>不严格次小生成树</h2>
首先,一定有一种非常暴力的算法,能求出最小生成树。显而易见:我们先求出MST,然后每次加入一条边,求一遍MST,取最小值即可。
但是这样来看,时间复杂度非常高,每枚举一条边,就需要跑一遍最小生成树。时间复杂度趋近于$ N^2log_2N $。
这个复杂度我们是不能接受的。
那么,我们来看一下算法二。
我们考虑我们每次在最小生成树中加入一条边,这个最小生成树就会变成一个环。怎么让它成为次小呢?首先,"次"已经做到,因为已经加入了一条新的非原最小生成树的边。然后我们需要做到“小”。不难发现,为了使该树的权值尽量的少,我们需要去除一条最大的非新加入的边。
那么,现在就成功的将这个问题转化到了对于一棵树,快速处理出$ (x,y) $点对的路径中的最大权值。
那么,我们可以通过树上动归或LCA或乱搞等,来在$ N^2 $的时间内来处理(当然更优更好)。由于本人太菜,只会LCA。
那么,恭喜您已经掌握了不严格次小生成树的做法了。
<h2>严格次小生成树</h2>
考虑为什么不严格最小生成树是“不”严格的。
因为它允许权值小于等于的生成树存在。
注意到“等于”,我们不是只需要去掉这个等于就可以了吗?
那么,记录次小值即可。
<h1>代码实现</h1>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <climits>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
#define LL long long
const int MAXN = 100000 + 5;
const int MAXM = 300000 + 5;
float log2N;
inline void Read(LL &x){
x = 0ll;char ch = getchar();
LL flag = 1ll;
for(;!isdigit(ch);ch = getchar())
if(ch == '-') flag = -1ll;
for(;isdigit(ch);ch = getchar())
x = x * 10 + (ch ^ 48);
x *= flag;
}
LL N,M,mst;
struct Data{
LL u,v,w;
bool inmst;
bool operator < (const Data &other) const {
return w < other.w;
}
}e[MAXM];
struct Node{
struct Edge *firstEdge;
LL num,depth;
bool vis;
}node[MAXN];
struct Edge{
Node s,t;
LL w;Edge *next;
}pool[MAXN 2],frog = pool;
Edge New(Node s,Node *t,LL w){
Edge *ret = ++frog;
ret->s = s;ret->t = t;
ret->w = w;ret->next = s->firstEdge;
return ret;
}
inline void add(LL u,LL v,LL w){
node[u].firstEdge = New(&node[u],&node[v],w);
node[v].firstEdge = New(&node[v],&node[u],w);
}
LL fa[MAXN];
LL find(LL x){
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void Kruskal(){
std::sort(e + 1,e + M + 1);
for(LL i = 1;i <= N;i++)
fa[i] = i;
LL cnt = 0;
for(LL i = 1;i <= M;i++){
LL fu = find(e[i].u),fv = find(e[i].v);
if(fu == fv) continue;
fa[fv] = fu;
e[i].inmst = true;
mst += e[i].w;
add(e[i].u,e[i].v,e[i].w);
if(++cnt == N - 1) break;
}
}
LL fMAXN,maxMAXN,minMAXN;
//max记录最大值,min记录次大值。
void dfs(Node *v){
v->vis = true;
LL now = v->num;
for(int i = 1;i <= log2N;i++){
if(v->depth <= (1 << i)) break;
fnow = ff[now][i-1];
maxnow = std::max(maxnow,maxf[now][i-1]);
minnow = std::max(minnow,minf[now][i-1]);
if(maxnow > maxf[now][i-1])
minnow = std::max(minnow,maxf[now][i-1]);
else if(maxnow < maxf[now][i-1])
minnow = std::max(minnow,maxnow);
}
for(Edge *e = v->firstEdge;e;e = e->next){
if(!e->t->vis){
e->t->depth = v->depth + 1ll;
maxe->t->num = e->w;
mine->t->num = LLONG_MIN;
fe->t->num = now;
dfs(e->t);
}
}
}
inline LL lca(LL x,LL y){
LL dx = node[x].depth,dy = node[y].depth;
if(dx != dy){
if(dx < dy){
std::swap(x,y);
std::swap(dx,dy);
}
int d = dx - dy;
for(int i = 0;i <= log2N;i++){
if((1 << i) & d) x = fx;
}
}
if(x == y) return x;
for(int i = log2N;i >= 0;i--){
if(nodef[x].depth < 0) continue;
if(fx == fy) continue;
x = fx;y = fy;
}
return fx;
}
inline LL query(LL u,LL v,LL maxx){
LL ans = LLONG_MIN;
for(int i = log2N;i >= 0;i--){
if(nodef[u].depth >= node[v].depth){
if(maxx != maxu) ans = std::max(ans,maxu);
else ans = std::max(ans,minu);
u = fu;
}
}
return ans == LLONG_MIN ? 0 : ans;
}
inline void init(){
memset(f,-1,sizeof(f));
for(int i = 1;i <= N;i++){
node[i].vis = false;
node[i].depth = 0;
node[i].num = i;
}
log2N = log(N) / log(2) + 1;
min1 = LLONG_MIN;
node[1].depth = 1;
}
int main(){
Read(N);Read(M);
for(LL i = 1;i <= M;i++){
Read(e[i].u);Read(e[i].v);Read(e[i].w);
}
init();
Kruskal();
dfs(&node[1]);
LL ans = LLONG_MAX;
for(int i = 1;i <= M;i++){
if(!e[i].inmst){
LL lc = lca(e[i].u,e[i].v);
LL d = e[i].w;
//DEBUG(lc);DEBUG(d);
ans = std::min(ans,mst - std::max(query(e[i].u,lc,e[i].w),query(e[i].v,lc,e[i].w)) + e[i].w);
//DEBUG(ans);
}
}
printf("%lld",ans);
//getchar();getchar();
return 0;
}