• 【题解】严格次小生成树


    题目链接

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

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

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

    先跑一边最小生成树。

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

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

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

    LCT

    树上倍增。

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

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

    那么我们的代码复杂度也就是约(O(nlog 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
    ",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.)

  • 相关阅读:
    iOS 网络编程:socket
    Zsh和Bash,究竟有何不同
    关于微软win10 2004的更新以及wsl2 Ubuntu18.04安装
    C# 委托事件机制 订阅发布
    关于Docker理念和安装,对Visual Studio2019自带生成的DockerFile配置,以及Docker镜像的发布与拉取
    div弹框
    vs分页
    ado显示下拉
    SQL server动态拼接存储过程分页
    退役
  • 原文地址:https://www.cnblogs.com/h-lka/p/12218606.html
Copyright © 2020-2023  润新知