2020.10.24--strict sub-small spanning tree--detailed explanation


2020-1024=996

Fine product


10.24祝各位coder编码有成,学业进步,工作顺利,升职加薪!!!

Pre-knowledge
1. Prim algorithm and Kurskal algorithm to find the minimum spanning tree
2. LCA to find the nearest common ancestor between two points on the tree

concept

If the minimum spanning tree edge set is selected EM, a small set of edges Strictly spanning tree is selected ES, you need to meet:

Insert picture description here

The idea
requires the second-smallest spanning tree, that is, the operation of adding and deleting edges on the minimum spanning tree to obtain the second-smallest spanning tree

If we find the second-smallest spanning tree, and we arbitrarily take an edge between two points u and v on the tree, then a ring must be generated at this time . We need to delete an edge from this ring to maintain the structure of the tree. Then the weight of the edge I deleted and the added edge should be as equal as possible (but not equal), because in this way, the weight of the new tree may increase less.

Step
1. Find the minimum spanning tree
2. Find the maximum and second largest value between any two points on the minimum spanning tree
3. Enumerate the extra edges, add them to the minimum spanning tree, and delete one from the generated ring Edge
4. Take the smallest of the weights of the new trees generated multiple times

Specific operation When
seeking the maximum value and the second maximum value between any two points, you can use the idea of ​​lca, the method of multiplication on the tree.
If the added edge connects the two points u and v, then we can find the maximum or the second largest value from u to lca respectively. The same is true for v, and then the two are the largest.
Because the weight of a[i].w of the added edge must be greater than or equal to the weight of the largest edge on the tree, if the maximum value of the bright spot on the tree is equal to a[i].w, we take the second smallest value , Only in this way can the weight of the new tree increase as small as possible

note

The second largest value between any two points on the tree should be initialized to negative infinity! ! !

If not, because if the edge weights in the minimum spanning tree are all equal, and then the weight of an edge and the weight on the tree are also equal, the answer is min-0+w;
then there is another edge The weight of is (w+10) and the answer is min-(w)+(w+10);

Because the former is not an answer, but it will be judged as an answer, because he is small (under certain circumstances where w is satisfied)

(Draw a tree by yourself to understand, it's relatively simple)

Code

#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>

using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const int maxn = 8e5 + 7;
const ll mod = 1000000007;

#define mst(x, a) memset( x,a,sizeof(x) )
#define rep(i, a, b) for(int i=(a);i<=(b);++i)
#define dep(i, a, b) for(int i=(a);i>=(b);--i)

ll read() {
    
    
	ll x = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9')ch = getchar();
	while(ch >= '0' && ch <= '9')x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return x;
}

void out(ll x) {
    
    
	int stackk[40];
	if (x < 0) {
    
    
		putchar('-');
		x = -x;
	}
	if (!x) {
    
    
		putchar('0');
		return;
	}
	int top = 0;
	while (x) stackk[++top] = x % 10, x /= 10;
	while (top) putchar(stackk[top--] + '0');
}
ll n,m,p[maxn],cnt,head[maxn],fa[maxn][22],max1[maxn][22];
ll max2[maxn][22],dep[maxn],len,vis[maxn],paid;
struct node {
    
    
	ll u,v,w;
} a[maxn];
bool cmp(node x,node y) {
    
    
	return x.w<y.w;
}
struct nodee {
    
    
	ll u,v,w,next;
} e[maxn];
void add(ll u,ll v,ll w) {
    
    
	e[cnt].u=u,e[cnt].v=v;
	e[cnt].w=w,e[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(ll u,ll p) {
    
    
	dep[u]=dep[p]+1,fa[u][0]=p;
	for(int i=1 ; i<=len ; i++) fa[u][i]=fa[fa[u][i-1]][i-1];
	for(int i=head[u]; ~i; i=e[i].next) {
    
    
		int v=e[i].v;
		if(v==p) continue;
		max1[v][0]=e[i].w;
		dfs(v,u);
	}
}
void cal() {
    
    
	for(int i=1 ; i<=len; i++) {
    
    
		for(int j=1 ; j<=n; j++)   {
    
    
			max1[j][i]=max(max1[j][i-1],max1[fa[j][i-1]][i-1]);
			max2[j][i]=max(max2[j][i-1],max2[fa[j][i-1]][i-1]);
			if(max1[j][i-1]<max1[fa[j][i-1]][i-1]) max2[j][i]=max1[j][i-1];
			else    if(max1[j][i-1]>max1[fa[j][i-1]][i-1]) max2[j][i]=max1[fa[j][i-1]][i-1];
		}
	}
}
ll lca(ll x,ll y) {
    
    
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=len ; i>=0 ; i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	if(x==y) return x;
	for(int i=len ; i>=0 ; i--)
		if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
ll q(ll x,ll y,ll w) {
    
    
	ll ans=0;
	for(int i=len ; i>=0 ; i--)
		if(dep[fa[x][i]]>=dep[y]) {
    
    
			if(max1[x][i]!=w) ans=max(ans,max1[x][i]);
			else ans=max(ans,max2[x][i]);
			x=fa[x][i];
		}
	return ans;
}
ll find(ll x) {
    
    
	if(p[x]==x) return x;
	return p[x]=find(p[x]);
}
int main() {
    
    
	mst(head,-1);
	n=read(),m=read();
	len=log2(n)+2;
	for(int i=0 ; i<=n ; i++) p[i]=i;
	for(int i=1 ; i<=m ; i++) {
    
    
		a[i].u=read();
		a[i].v=read();
		a[i].w=read();
	}
	sort(a+1,a+1+m,cmp);
	for(int i=1 ; i<=m ; i++) {
    
    
		ll u=a[i].u;
		ll v=a[i].v;
		if(find(u)==find(v)) continue;
		p[find(u)]=find(v);
		add(u,v,a[i].w);
		add(v,u,a[i].w);
		vis[i]=1,paid+=a[i].w;
	}
	max2[1][0]=-1e17;
	dfs(1,-1);
	cal();
	ll ans=1e17;
	for(int i=1 ; i<=m ; i++) {
    
    
		if(vis[i]) continue;
		ll u=a[i].u;
		ll v=a[i].v;
		ll lc=lca(u,v);
		ll temp1=q(u,lc,a[i].w);
		ll temp2=q(v,lc,a[i].w);
		ans=min(ans,paid-max(temp1,temp2)+a[i].w);
	}
	printf("%lld",ans);
	return 0;
}
/*


*/

Guess you like

Origin blog.csdn.net/wmy0536/article/details/109255719