zoukankan      html  css  js  c++  java
  • 次小生成树

    非严格次小生成树

    在无向图中,边权和最小的满足边权和 大于等于 最小生成树边权和的生成树

    如何求解?
    先求出最小生成树,设其权值和为 (sum)

    首先要知道,对于 (u,v) 两点,他们在最小生成树上的路径中,权值最大值肯定要小于等于边 ((u,v)) 的权值(如果有的话)
    因为如果他们路径上的最大值比 ((u,v)) 边权大,那么肯定就要去掉那条最大的边,换成 ((u,v)),这样做一样能保证是一颗树,且边权和更小

    那么,可以依次对于每条边 ((u,v,w)),找出 ((u,v)) 在最小生成树上的权值最大值,设其为 (max_w)
    我们尝试用 ((u,v,w)) 来替换掉找出的那条边权最大的边
    则可以用 (sum-max_w+w) 来更新答案
    答案的最小值,也就是 (min(sum-max_w+w)) 即为最终答案

    这样做一定能找到非严格次小生成树之一
    因为在所有非严格次小生成树中(显然可能有多个),一定会有一个是由最小生成树仅更换一条边得来
    由于上面说的,(u,v) 两点,他们在最小生成树上的路径中,权值最大值肯定要小于等于边 ((u,v)) 的权值,所以可以大体的理解为 “更换的边越多,权值和会变得越大”
    当然这并不是严格的证明

    对于如何找边权最大的那条边,在最小生成树上用倍增维护即可
    由于没有题,代码没写

    严格次小生成树

    在无向图中,边权和最小的满足边权和 严格大于 最小生成树边权和的生成树

    考虑为什么上面个说的方法求出的次小生成树“不严格”
    (u,v) 两点,他们在最小生成树上的路径中,权值最大值肯定要 小于等于((u,v)) 的权值
    这句话复制了第三遍了
    是小于等于,所以如果最小生成树中,被替换掉的那条边,和替换他的那条边,权值相等的话,求出的次小生成树就和最小生成树的权值和相等
    因此不严格

    所以,只要倍增时,维护一个最小生成树上路径中的最大值,同时也维护次大值
    如果最大值和 ((u,v)) 的边权相等,那么就用次大值更新

    这样就保证了找出的次小生成树严格大于最小生成树

    题目:luogu P4180 [BJWC2010]严格次小生成树LOJ#10133. 「一本通 4.4 例 4」次小生成树
    另外代码中有个玄学问题,题目范围事 (nle 10^5),但卡着比 (10^5) 多点并不能过,会在最后一个点越界
    要再开大一些,听说是因为最后一个点有自环,但是我把自环判掉,如果不开大数组还是不能过
    真 · 玄 学
    所以就往大里开就好了

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    #define N 1000006
    #define M 300006
    int n,m;
    struct data{
    	int u,v,val;
    }e[M];
    int up[N],on_tree[N];
    int fir[N],nex[2*N],to[2*N],w[2*N],tot;
    int max[23][N],max2[23][N];
    int deep[N],fa[23][N];
    inline void add(int u,int v,int val){
    	to[++tot]=v;w[tot]=val;
    	nex[tot]=fir[u];fir[u]=tot;
    }
    inline int cmp(data aa,data aaa){return aa.val<aaa.val;}
    inline int find(int x){
    	return up[x]==x?x:up[x]=find(up[x]);
    }
    inline LL kruskal(){
    	std::sort(e+1,e+1+m,cmp);
    	reg LL sum=0;
    	for(reg int i=1;i<=n;i++) up[i]=i;
    	for(reg int u,v,i=1,cnt=0;cnt<n-1;i++){
    		u=find(e[i].u);v=find(e[i].v);
    		if(u==v) continue;
    		up[u]=v;cnt++;
    		add(e[i].u,e[i].v,e[i].val);add(e[i].v,e[i].u,e[i].val);
    		sum+=e[i].val;
    		on_tree[i]=1;
    	}
    	return sum;
    }
    void dfs(int u,int father){
    	deep[u]=deep[father]+1;fa[0][u]=father;
    	max2[0][u]=-1e9;//不存在次大值时,把次大值设为 -inf
    	for(reg int v,i=fir[u];i;i=nex[i]){
    		v=to[i];
    		if(v==father) continue;
    		max[0][v]=w[i];
    		dfs(v,u);
    	}
    }
    inline void pre(){
    	int tmp[4];
    	for(reg int i=1;i<=20;i++)
    		for(reg int j=1;j<=n;j++){
    			fa[i][j]=fa[i-1][fa[i-1][j]];
    			tmp[0]=max[i-1][j];tmp[1]=max[i-1][fa[i-1][j]];
    			tmp[2]=max2[i-1][j];tmp[3]=max2[i-1][fa[i-1][j]];
    			//最大和次大值从上面四个值中得出
    			std::sort(tmp,tmp+4);
    			max[i][j]=tmp[3];
    			int pos=2;
    			while(pos>=0&&tmp[pos]==tmp[3]) pos--;
    			max2[i][j]=pos==-1?-1e9:tmp[pos];
    		}
    }
    inline int get_lca(int a,int b){
    	if(deep[a]<deep[b]) a^=b,b^=a,a^=b;
    	for(reg int i=20;~i;i--)
    		if(deep[fa[i][a]]>=deep[b]) a=fa[i][a];
    	if(a==b) return a;
    	for(reg int i=20;~i;i--)if(fa[i][a]!=fa[i][b]){
    		a=fa[i][a];b=fa[i][b];
    	}
    	return fa[0][a];
    }
    inline int get_max(int a,int b,int val){
    	int ret=-1e9;
    	for(reg int i=20;~i;i--)if(deep[fa[i][a]]>=deep[b]){
    		if(val!=max[i][a]) ret=std::max(ret,max[i][a]);
    		else ret=std::max(ret,max2[i][a]);
    		a=fa[i][a];
    	}
    	return ret;
    }
    int main(){
    	n=read();m=read();
    	for(reg int i=1;i<=m;i++){
    		e[i].u=read();e[i].v=read();e[i].val=read();
    	}
    	LL sum=kruskal(),ans=2e18;
    	dfs(1,0);
    	pre();
    	for(reg int i=1;i<=m;i++)if(!on_tree[i]){
    		int lca=get_lca(e[i].u,e[i].v);
    		LL tmpu=get_max(e[i].u,lca,e[i].val);
    		LL tmpv=get_max(e[i].v,lca,e[i].val);
    		if(std::max(tmpu,tmpv)>-1e9)
    			ans=std::min(ans,sum-std::max(tmpu,tmpv)+e[i].val);
    	}
    	std::printf("%lld",ans==2e18?-1:ans);
    	return 0;
    }
    
  • 相关阅读:
    86. Partition List
    328. Odd Even Linked List
    19. Remove Nth Node From End of List(移除倒数第N的结点, 快慢指针)
    24. Swap Nodes in Pairs
    2. Add Two Numbers(2个链表相加)
    92. Reverse Linked List II(链表部分反转)
    109. Convert Sorted List to Binary Search Tree
    138. Copy List with Random Pointer
    为Unity的新版ugui的Prefab生成预览图
    ArcEngine生成矩形缓冲区
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/12790262.html
Copyright © 2011-2022 走看看