# Strictly sub small spanning tree

Keywords: C++

## Title Description

Recently, little C has learned many algorithms of minimum spanning tree, such as Prim algorithm, Kurskal algorithm, loop elimination algorithm, etc. Just when little C is complacent, little p pours cold water on little c again. Small P said that let small C find a small spanning tree of undirected graph, and this small spanning tree must be strictly small.

Now little C Meng has found you. I hope you can help him solve this problem.

## Input format

The first row contains two integers, N and M, representing the number of points and edges of an undirected graph. Next row M, each row is represented by three numbers x y z. there is an edge between point X and point y, and the weight of the edge is z.

## Output format

It contains one row and only one number, representing the sum of edge weights of strictly sub small spanning trees. (data guarantee must have strict sub small spanning tree)

Luo Valley P4180

Analysis: the first thing I think about when I see this problem is enumeration. I want to enumerate all the spanning trees to get the minimum one time, but obviously it can't, because the data is a little big.

Think about the minimum spanning tree algorithm? Kurskal realizes the minimum tree by adding the minimum edge every time, so let's consider the next edge.

For a minimum spanning tree, deleting the upper side of the tree and adding an edge maintenance tree may form a sub small spanning tree. To ensure that it is strict, it is necessary to judge whether the edge is equal to the deleted edge.

Now we are thinking about deleting edges. How can we ensure that the deleted edges plus the edges are still trees? Obviously, first add an edge, and then look at the largest edge in the ring formed by this edge and other edges. Why use the largest edge? Because it requires a sub small spanning tree. Near a point, after adding an edge, the remaining edge must be greater than or equal to the added edge. Otherwise, according to Kurskal, this edge will not be added, but this edge may be equal to the added edge, so it is the largest edge in the record Record the second big side as well as the big side.

The problem of deleting and adding edges has been solved, and then there are records. I didn't expect to use lca for records.

Why use lca? The ring formed after adding an edge can be divided into two parts, one is from the edge to lca, and the other is from the edge to lca. Therefore, the maximum value and the next maximum value can be maintained (by the way) while doubling the lca.

Finally, it is the calculation. The calculation is also divided into two parts. Remember to judge whether the maximum value is the same as the edge weight, and if so, use the next maximum value.

```#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
//Forward star margin
struct Edge{
int to,from,next,val;
bool isin;
Edge(){isin=val=next=0;}
bool operator < (const Edge &A)const {
return val<A.val;
}
}e[N<<1],E[N*3];
void Ins(int a,int b,int c){
e[++len].to=b;e[len].val=c;
}
//Union checking set+Kurskal
int f[N],m,n;
int find(int x){
return f[x]==x?x:(f[x]=find(f[x]));
}
int ans=0x3f3f3f3f;long long tot=0;
void Krs(){
int cnt=1;
sort(E+1,E+m+1);
for(int i=1;cnt<n;i++){
int v=E[i].to,u=E[i].from;
if(find(v)!=find(u)){
cnt++;
E[i].isin=1;
tot+=E[i].val;//Calculate minimum spanning tree
Ins(u,v,E[i].val);
Ins(v,u,E[i].val);
f[find(v)]=find(u);
}
}
}
//multiplication lca Board
int dep[N],p[N],Max[N],Smax[N];
void dfs(int x){
for(int i=0;p[x][i];i++){
p[x][i+1]=p[p[x][i]][i];
Max[x][i+1]=max(Max[x][i],Max[p[x][i]][i]);
//Pay attention to check whether the maximum value of two segments is equal when finding the second largest, if not
//When the maximum value is equal, the next maximum value is updated to the maximum value
if(Max[x][i]==Max[p[x][i]][i])
Smax[x][i+1]=max(Smax[x][i],Smax[p[x][i]][i]);
else
Smax[x][i+1]=max(min(Max[x][i],Max[p[x][i]][i]),
max(Smax[x][i],Smax[p[x][i]][i]));
}
int v=e[i].to;
if(v!=p[x]){
dep[v]=dep[x]+1;
Max[v]=e[i].val;
Smax[v]=-1;
p[v]=x;
dfs(v);
}
}
}
//This paragraph and lca The board is as like as two peas.
int lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
int d=dep[a]-dep[b];
for(int i=0;d;i++,d>>=1)
if(d&1)a=p[a][i];
if(a==b)return a;
for(int i=18;i>=0;i--)
if(p[a][i]!=p[b][i])
a=p[a][i],b=p[b][i];
return p[a];
}
//Calculation
void calc(int u,int v,int w){
int d=dep[u]-dep[v];//Depth difference, judge path
int m1=0,m2=0;
for(int i=0;d;i++,d>>=1){
if(d&1){//If you can jump up
m2=max(m2,Smax[u][i]);//Detail, find the next largest value first
if(Max[u][i]>m1){//If the maximum value is updated, the original maximum value m1 May be a new sub maximum
m2=max(m2,m1);//Determine whether the next largest value is updated
m1=Max[u][i];//Update Max
}
}
}
if(m1==w)ans=min(ans,w-m2);//If the maximum value is equal to the edge, update with the next maximum value
else ans=min(ans,w-m1);//Otherwise, use the maximum value
}
int main(){
//    freopen("a.txt","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&E[i].from,&E[i].to,&E[i].val);
Krs();
dfs(1);
for(int i=1;i<=m;i++){
if(!E[i].isin){
int u=E[i].from,v=E[i].to,Lca;
Lca=lca(u,v);
calc(u,Lca,E[i].val);
calc(v,Lca,E[i].val);
}
}
printf("%lld\n",tot+ans);
}```

Posted by meow on Sat, 07 Mar 2020 22:05:49 -0800