suxxsfe

一言(ヒトコト)

次小生成树

非严格次小生成树

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

如何求解?
先求出最小生成树,设其权值和为 \(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」次小生成树
另外代码中有个玄学问题,题目范围事 \(n\le 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;
}
posted @ 2020-04-27 21:24  suxxsfe  阅读(119)  评论(0编辑  收藏  举报