(严格)次小生成树

思想很简单:

先建出一棵最小生成树,满足使用的边都是最小的,剩下的边(称为非树边)一定没有树边优。

如果我们加入一条非树边,删除最小生成树中的一条边,次小生成树一定是包括在以这种方法建出的树中的(倘若删两条树边加两条非树边,则肯定没有删一条加一条优,绝不是次小生成树)。

做法:

对于每一条非树边,其边的两端点为 (\(x\) , \(y\)),加入到最小生成树中,则一定会构成一个环。

这时删除环中除了新边以外最大的边,就构成了新的生成树。

删去\(\Downarrow\)

则新树有可能是次小生成树。

可以发现,在环中找最大边可以看为找 \(x\Rightarrow y\) 路径上的最大边。

即从x,y找LCA的同时记录最大边

可以用树上倍增、树剖来维护。

对于严格次小生成树,再维护一个次大边,在最大边等于新边时,删去次大边。

严格次小生成树code:

坑好多

const int N=1e6+10;

int n,m,x,y,z,k,tt,cnt,mint,dep[N],fa[N],head[N],jd[N],mx[N][24],mxx[N][24],f[N][24];
struct node{
	int u,v,w,nxt;
}a[N];
struct Node{
	int u,v,w;
}e[N];

void add(int u,int v,int w){
	a[++cnt]=(node){u,v,w,head[u]};
	head[u]=cnt;
}

int cmp(Node x,Node y){
	return x.w<y.w;
}

void dfs(int x,int fat){//分层 
	f[x][0]=fat;
	dep[x]=dep[fat]+1;
	for(int i=1;(1<<i)<=dep[x];++i){//Wrong
		f[x][i]=f[f[x][i-1]][i-1];
	}
	for(int i=head[x];i;i=a[i].nxt){
		int v=a[i].v;
		if(v==fat)continue;
		mx[v][0]=a[i].w;
		dfs(v,x);
	}
}

void init(){//倍增预处理 
	rep(j,1,21){
		for(int i=1;i+(1<<j)-1<=n;i++){
			mx[i][j]=Max(mx[i][j-1],mx[f[i][j-1]][j-1]);
			mxx[i][j]=Max(mxx[i][j-1],mxx[f[i][j-1]][j-1]);
			if(mx[i][j-1]!=mx[f[i][j-1]][j-1]) mxx[i][j]=Max(Min(mx[i][j-1],mx[f[i][j-1]][j-1]),mxx[i][j]);
		}
	}
}

int LCA(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	For(i,20,0)
	if(dep[f[x][i]]>=dep[y]) x=f[x][i];
	if(x==y)return x;
	For(i,20,0){
		if(f[x][i]!=f[y][i]){
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[x][0];
}

int Qmax(int u,int v,int w){
	int now=0;
	if(dep[u]<dep[v])swap(u,v);
	For(i,20,0){
		if(dep[f[u][i]]>=dep[v]){
			if(mx[u][i]==w) now=Max(now,mxx[u][i]);
			else now=Max(now,mx[u][i]);
			u=f[u][i];
			
		}
	}
	return now;
}

void work(){
	int ans=inf;
	rep(i,1,tt){//Wrong
		if(!jd[i]){
			int u=e[i].u,v=e[i].v,w=e[i].w;//Wrong
			int lca=LCA(u,v);
			int lmx=Qmax(u,lca,w),rmx=Qmax(v,lca,w);
			if(Max(lmx,rmx)!=w) ans=Min(ans,mint+w-Max(lmx,rmx));
		}
	}
	cout<<ans;
}

int find(int x){
	if(fa[x]==x)return x;
	fa[x]=find(fa[x]);
	return fa[x];
}

void un(int x,int y){
	int xx=find(x),yy=find(y);
	if(xx!=yy)
	fa[xx]=yy;
}

signed main(){
	n=read();m=read();
	rep(i,1,n) fa[i]=i;
	tt=0;
	rep(i,1,m){
		x=read();y=read();z=read();
		if(x==y) continue;//Wrong 未判自环 
		e[++tt].u=x;e[tt].v=y;e[tt].w=z;
	}
	sort(e+1,e+1+tt,cmp);
	rep(i,1,m){
		int u=e[i].u,v=e[i].v;
		if(find(u)!=find(v)){
			un(u,v);
			add(u,v,e[i].w);
			add(v,u,e[i].w);
			mint+=e[i].w;
			++k;
			jd[i]=1;
		}
		if(k==n-1) break;
	}
	dfs(1,0);
	init();
	work();
	return 0;
}
posted @ 2023-06-19 19:34  XYini  阅读(1)  评论(0)    收藏  举报