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;
    }
    
  • 相关阅读:
    C# 枚举常用工具方法
    AppBox_v3.0
    DDD:四色原型中Role的 “六” 种实现方式和PHP的Swoole扩展
    MySql主从配置实践及其优势浅谈
    ActionInvoker
    【Oracle】-【体系结构】-【DBWR】-DBWR进程相关理解
    Linux MySQL单实例源码编译安装5.6
    窗口嵌入到另一个窗口(VC和QT都有)
    Window下 Qt 编译MySQL驱动(居然用到了动态库格式转换工具,需要将C:/MySQL/lib目录下的libmySQL.dll文件复制到我们Qt Creator安装目录下的qt/bin目录中)good
    在Linux下使用iconv转换字符串编码
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/12790262.html
Copyright © 2011-2022 走看看