【题解】严格次小生成树

题目链接

题目大意:求一个严格的次小生成树,使得其边权和严格小于最小生成树。

(本来想着\(LCT\)但笔者太菜)

我们可以试着想一个暴力思路:枚举每一条加进去的边,看看替换怎么样,记录下每一次替换后的生成树边权和,然后比较答案。

先跑一边最小生成树。

考虑我们替掉树上的哪一条边。

因为我们加进去的应该是那条边所在环上的最大的边,我们替的话应该\(instead\) \(of\) 最大的边。

树上维护最大边权,想到了啥?

LCT

树上倍增。

我们可以倍增思路,预处理出来点\(i\)到它的\(2^{j}\)次方父亲的路径中的最大值。

那么我们找树上两点最大值的时间复杂度就变成了\(\log(n)\)级别。

那么我们的代码复杂度也就是约\(O(n\log n)\)

可以通过本题。

注意:连边的时候是两次,所以排序的时候要排\((m+m)\)条边,倍增处理的时候\((1<<31)\)会爆\(int\),以及注意初始\(dfs\)的传参……

最大值递推式:

\(fm[i][j]=max(fm[i][j-1],fm[fa[i][j-1]][j-1])\)

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}
const int MAXN=5e5+10;
const int inf=(1LL<<60);
int H[MAXN<<1],tot,head[MAXN],cnt;
int n,m,fa[MAXN][31],fm[MAXN][31];
int f[MAXN],vis[MAXN],MAY,sum,ans;
int dep[MAXN];
struct edge{
	int nxt,to,dis,pre;
}g[MAXN<<1],e[MAXN];
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
inline void add(int x,int y,int dis){
	e[++tot].to=y;
	e[tot].nxt=head[x];
	e[tot].dis=dis;
	e[tot].pre=x;
	head[x]=tot;
}
inline void kdd(int x,int y,int dis){
	g[++cnt].to=y;
	g[cnt].nxt=H[x];
	g[cnt].dis=dis;
	g[cnt].pre=x;
	H[x]=cnt;
}
inline bool cmp(edge a,edge b){return a.dis<b.dis;}
void kruskal(){
	sort(g+1,g+m+m+1,cmp);
	for(int i=1;i<=2*m;++i){
		int x=g[i].pre,y=g[i].to;
		int fx=find(x),fy=find(y);
		if(fx==fy)continue;add(y,x,g[i].dis);sum+=g[i].dis;
		f[fx]=fy;add(x,y,g[i].dis);vis[i]=1;vis[i+1]=1;
	}
}
void dfs(int x,int father,int L){
	fa[x][0]=father;
	fm[x][0]=L;
	dep[x]=dep[father]+1;
	for(int i=1;(1<<i)<=dep[x];++i){
		fa[x][i]=fa[fa[x][i-1]][i-1];
		fm[x][i]=max(fm[x][i-1],fm[fa[x][i-1]][i-1]);
	}
	for(int i=head[x];i;i=e[i].nxt){
		int j=e[i].to;
		if(j==father)continue;
		dfs(j,x,e[i].dis);
	}
}
void LCA(int x,int y,int &SS,int val){
	int res=-1e18;
	if(dep[x]<dep[y])swap(x,y);
	int X=x,Y=y;
	int cha=dep[x]-dep[y];
	for(int i=30;i>=0;--i)
		if(cha>=(1<<i)){
			cha-=(1<<i);
			if(fm[x][i]!=val)res=max(res,fm[x][i]);
			x=fa[x][i];
		} 	
	if(x==y){
		SS-=res;SS+=val;
		return;
	}
	for(int i=30;i>=0;--i)
		if(fa[x][i]!=fa[y][i]){
			if(fm[x][i]!=val)res=max(res,fm[x][i]);
			if(fm[y][i]!=val)res=max(res,fm[y][i]);
			x=fa[x][i],y=fa[y][i];
		}		
	if(fm[x][0]!=val)res=max(res,fm[x][0]);
	if(fm[y][0]!=val)res=max(res,fm[y][0]);
	SS-=res;SS+=val;
}
void solve(){
	ans=inf;
	int s=0;
	for(int i=1;i<=m+m;++i){
		if(!vis[i]){
			int x=g[i].pre,y=g[i].to;
			LCA(x,y,(s=sum),g[i].dis);
			ans=min(ans,s);
			vis[i+1]=1;
		}
	}
	printf("%lld\n",ans);
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i)f[i]=i;
	for(int i=1;i<=m;++i){
		int x,y,z;
		x=read(),y=read(),z=read();
		kdd(x,y,z);kdd(y,x,z);
	} 
	kruskal();
	dfs(1,0,0);
	solve();
	return 0;
}

注意开\(long\) \(long\).

\(Over.\)

posted @ 2020-01-20 16:56  Refined_heart  阅读(153)  评论(0编辑  收藏  举报