次小生成树

给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。

设最小生成树的边权之和为sum,严格次小生成树就是指边权之和大于sum的生成树中最小的一个。

输入格式

第一行包含两个整数N和M。

接下来M行,每行包含三个整数x,y,z,表示点x和点y之前存在一条边,边的权值为z。

输出格式

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

数据范围

N≤105,M≤3∗105N≤105,M≤3∗105

输入样例:

5 6 
1 2 1 
1 3 2 
2 4 3 
3 5 4 
3 4 3 
4 5 6 

输出样例:

11

思路

原理和"秘密的牛奶运输"一样。不过这道题的点数和边数大了一个级别,不能爆力枚举树上两点之间的最长边.

而是通过倍增lca预处理点到祖先节点的最长边,道理一样\(fa[u][i]\)表示节点u的第\(2^i\)个祖先,\(d1[u][i],d2[u][i]\)表示u到第\(2^i\)和祖先节点的最长和次长边的长度。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=100010,M=300010,INF=0x3f3f3f3f;
typedef long long LL;

int h[N],e[N*2],w[N*2],nex[N*2],idx,n,m;
struct edge{
	int x,y,z;
	bool used;
	bool operator < (const edge & b) const {
		return z < b.z;
	}
}eg[M];
void add(int u,int v,int t){
	e[idx]=v;
	w[idx]=t;
	nex[idx]=h[u];
	h[u]=idx++;
}
int f[N];
int Find(int x){
	return f[x]==x ? x:f[x]=Find(f[x]);
}
LL kruskal(){
	sort(eg+1,eg+1+m);
	LL sum=0; 
	memset(h,-1,sizeof h);
	for(int i=1;i<=m;++i){
		int x=eg[i].x,y=eg[i].y,t=eg[i].z;
		int fx=Find(x),fy=Find(y);
		if(fx!=fy){
			f[fx]=fy;
			eg[i].used=1;
			add(x,y,t);
			add(y,x,t);
			sum+=t;
		}
	}
	return sum;
}
int fa[N][20],d1[N][20],d2[N][20],q[N],dep[N];
void bfs(){
	int hh=0,tt=0;
	q[tt++]=1;
	memset(dep,0x3f,sizeof dep);
	dep[0]=0,dep[1]=1;
	while(hh!=tt){
		int u=q[hh++];
		for(int i=h[u];~i;i=nex[i]){
			int v=e[i],t=w[i];
			if(dep[v]>dep[u]+1){
				dep[v]=dep[u]+1;
				q[tt++]=v;
				fa[v][0]=u;
				d1[v][0]=t,d2[v][0]=-INF;
				for(int j=1;j<18;++j){
					int anc=fa[v][j-1];
					fa[v][j]=fa[anc][j-1];
				    int dis[4]={d1[v][j-1],d2[v][j-1],d1[anc][j-1],d2[anc][j-1]};
					d1[v][j]=d2[v][j]=-INF;
					for(int k=0;k<4;++k){
						int d=dis[k];
						if(d>d1[v][j]){
							d2[v][j]=d1[v][j];
							d1[v][j]=d;
						}
						else if(d!=d1[v][j]&&d>d2[v][j]){
							d2[v][j]=d;
						}
					}
				}
			}
		}
	}
}
int lca(int x,int y,int w){
	if(dep[x]<dep[y]) swap(x,y);
	static int dis[N*2];
	int  cnt=0;
	for(int i=17;i>=0;--i){
		if(dep[fa[x][i]]>=dep[y]){
			dis[cnt++]=d1[x][i];
			dis[cnt++]=d2[x][i];
			x=fa[x][i];
		}
	}
	if(x!=y){
		for(int i=17;i>=0;--i){
			if(fa[x][i]!=fa[y][i]){
				dis[cnt++]=d1[x][i];
				dis[cnt++]=d2[x][i];
				dis[cnt++]=d1[y][i];
				dis[cnt++]=d2[y][i];
				x=fa[x][i],y=fa[y][i];
			}
		}
		dis[cnt++]=d1[x][0];
		dis[cnt++]=d2[y][0];
	}
	int dist1,dist2;
	dist1=dist2=-INF;
	for(int i=0;i<cnt;++i){
		int d=dis[i];
		if(d>dist1) dist2=dist1,dist1=d;
		else if(d!=dist1&&d>dist2) dist2=d;
	}
	if(w!=dist1) return dist1;
	else return dist2;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) f[i]=i;
	for(int i=1;i<=m;++i){
		int x,y,t;
		scanf("%d%d%d",&x,&y,&t);
		eg[i]={x,y,t,0};
	}
	LL sum=kruskal();
	bfs();
	LL ans=1e18;
	for(int i=1;i<=m;++i){
		if(!eg[i].used){
			int x=eg[i].x,y=eg[i].y,z=eg[i].z;
			ans=min(ans,sum+z-lca(x,y,z));
		}
	}
	cout<<ans<<endl;
	return 0;
} 
posted @ 2020-05-15 14:30  0x4f  阅读(167)  评论(0编辑  收藏  举报