• 次小生成树


    给定一张 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;
    } 
    
  • 相关阅读:
    数组的反转和二维数组
    初识数组
    Python学习笔记-Day8
    Python学习笔记-Day7
    Python学习笔记-Day6
    Python学习笔记-Day5
    Python学习笔记-Day4
    Python学习笔记-Day3
    Python学习笔记-Day2
    Python学习笔记-Day1
  • 原文地址:https://www.cnblogs.com/jjl0229/p/12894766.html
Copyright © 2020-2023  润新知