次小生成树学习笔记

news/2024/7/15 2:10:11 标签: 图论

次小生成树有严格次小生成树和非严格次小生成树之分。常见的是严格次小生成树。

严格次小生成树的定义如下:

如果最小生成树选择的边集是 E M E_M EM,严格次小生成树选择的边集是 E S E_S ES,那么需要满足:( v a l u e ( e ) value(e) value(e) 表示边 e e e 的权值) ∑ e ∈ E M v a l u e ( e ) < ∑ e ∈ E S v a l u e ( e ) \sum_{e \in E_M}value(e)<\sum_{e \in E_S}value(e) eEMvalue(e)<eESvalue(e)

——P4180 [BJWC2010] 严格次小生成树


求严格次小生成树的方法主要有倍增、树剖、LCT 等。这里介绍所用算法较基础的 Kruskal+倍增+LCA 的方法。

显然,次小生成树是在最小生成树的基础上替换一条边之后形成的。

容易想到一种思路:对于每一条非树边,尝试把它加入最小生成树中。此时图中出现了一个环,删去环中边权最大的树边即可。由于要求的是严格次小,为了避免最大树边等于加入的这条非树边的边权的情况,还需要维护次大树边。

维护次大树边可以用倍增。用 g 1 ( i , j ) , g 2 ( i , j ) g_1(i,j),g_2(i,j) g1(i,j),g2(i,j) 分别表示从 i i i i i i 2 j 2^j 2j 级父亲的路径上的边权最大值、次大值。 g 1 g_1 g1 直接维护即可, g 2 g_2 g2 max ⁡ \max max 选择需要分情况讨论:

  • g 1 g_1 g1 ( i , i + 2 j − 1 ) , ( i + 2 j − 1 , i + 2 j ) (i,i+2^{j-1}),(i+2^{j-1},i+2^j) (i,i+2j1),(i+2j1,i+2j) 两段最大值相等,维护 g 2 g_2 g2 的两段最大值。
  • g 1 ( i , j − 1 ) > g 1 ( i + 2 j − 1 , j − 1 ) g_1(i,j-1)>g_1(i+2^{j-1},j-1) g1(i,j1)>g1(i+2j1,j1),则次大值不可能在后面的区间出现,维护前面区间的次大值和后面区间的最大值取 max ⁡ \max max 即可。
  • g 1 ( i , j − 1 ) < g 1 ( i + 2 j − 1 , j − 1 ) g_1(i,j-1)<g_1(i+2^{j-1},j-1) g1(i,j1)<g1(i+2j1,j1),则次大值不可能在前面的区间出现,维护后面区间的次大值和前面区间的最大值取 max ⁡ \max max 即可。

时间复杂度 O ( n log ⁡ n + m log ⁡ n ) O(n\log n+m\log n) O(nlogn+mlogn)

#include <bits/stdc++.h>
using namespace std;
#define int long long

const int maxn=1e5+5,maxm=6e5+5;
int head[maxn],g1[maxn][25],g2[maxn][25],f[maxn][25],dep[maxn],cnt,N,M,fa[maxn],sum;
struct edge1{int u,v,w;}e1[maxm];
struct edge2{int to,nxt,w;}e2[maxm];
bool vis[maxm];

bool cmp(edge1 a,edge1 b){return a.w<b.w;}

int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}

void add(int x,int y,int z){e2[++cnt]={y,head[x],z},head[x]=cnt;}

void kruskal()
{
	sort(e1+1,e1+M+1,cmp);
	int tot=0;
	for(int i=1;i<=M;i++)
	{
		int fu=find(e1[i].u),fv=find(e1[i].v);
		if(fu!=fv) tot++,fa[fu]=fv,sum+=e1[i].w,add(e1[i].u,e1[i].v,e1[i].w),add(e1[i].v,e1[i].u,e1[i].w),vis[i]=1;
		if(tot==N-1) break;
	}
}

void dfs(int x,int fa)
{
	dep[x]=dep[fa]+1,f[x][0]=fa;
	for(int i=1;i<=20;i++)
	{
		f[x][i]=f[f[x][i-1]][i-1];
		g1[x][i]=max(g1[f[x][i-1]][i-1],g1[x][i-1]);
		if(g1[f[x][i-1]][i-1]==g1[x][i-1]) g2[x][i]=max(g2[x][i-1],g2[f[x][i-1]][i-1]);
		else if(g1[f[x][i-1]][i-1]>g1[x][i-1]) g2[x][i]=max(g1[x][i-1],g2[f[x][i-1]][i-1]);
		else g2[x][i]=max(g2[x][i-1],g1[f[x][i-1]][i-1]);
	}
	for(int i=head[x];i;i=e2[i].nxt)
	{
		if(e2[i].to==fa) continue;
		g1[e2[i].to][0]=e2[i].w;
		dfs(e2[i].to,x);
	}
}

int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
	if(x==y) return x;
	for(int i=20;i>=0;i--)
		if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}

int getmx(int x,int rt,int w)
{
	int ans=0;
	for(int i=20;i>=0;i--)
		if(dep[f[x][i]]>=dep[rt])
		{
			if(g1[x][i]==w) ans=max(ans,g2[x][i]);
			else ans=max(ans,g1[x][i]);
			x=f[x][i];
		}
	return ans;
}

signed main()
{
	cin>>N>>M;
	for(int i=1;i<=M;i++) cin>>e1[i].u>>e1[i].v>>e1[i].w;
	for(int i=1;i<=N;i++) fa[i]=i;
	kruskal();
	dfs(1,0);
	int ans=LLONG_MAX;
	for(int i=1;i<=M;i++)
	{
		if(e1[i].u==e1[i].v||vis[i]) continue;
		int mx=getmx(e1[i].u,lca(e1[i].u,e1[i].v),e1[i].w),my=getmx(e1[i].v,lca(e1[i].u,e1[i].v),e1[i].w);
		if(max(mx,my)!=e1[i].w) ans=min(ans,sum+e1[i].w-max(mx,my));
	}
	cout<<ans<<endl;
	// for(int i=1;i<=N;i++) cout<<g1[i][0]<<' '<<g2[i][0]<<endl;
	return 0;
}

http://www.niftyadmin.cn/n/5154196.html

相关文章

QT在线安装所有版本,可共存(下载速度飞快)

使用最新的QT在线安装器&#xff0c;安装QT版本时只能安装5.15以及之后的版本&#xff0c;安装QT5.15之前的版本只能通过离线安装的方式&#xff0c;离线安装后还要自己去配置QT&#xff0c;离线安装还有个问题的&#xff0c;后续维护比较麻烦&#xff0c;QT的维护工具还要自己…

VueJs各个版本— 判断当前是开发、生产环境

VueJs各个版本— 判断当前是开发、生产环境 文章目录 VueJs各个版本— 判断当前是开发、生产环境vue项目分类VueCLI21&#xff0c;判断样例2&#xff0c;判断原理 Vue CLI 3 和 Vue CLI 41&#xff0c;判断样例2, 判断原理手动设置-json文件手动设置- .env 文件单个 .env 文件多…

“第六十一天”

这三个也算一类的&#xff0c;减和加的处理差不多&#xff0c;不过这个题多了限制是被减数大于减数&#xff0c;要是想再完整一点&#xff0c;可以把小于的情况也考虑进去&#xff0c;不过这个我是如果被减数小于减数的话&#xff0c;我就用减数加被减数&#xff0c;然后最后打…

【Linux】第十一站:冯诺依曼与操作系统

文章目录 一、冯诺依曼体系结构&#xff08;硬件层面&#xff09;1.冯诺依曼体系结构2.数据流向3.总结 二、操作系统1.概念2.设计操作系统的目的3.定位4.如何管理5.系统调用和库函数概念 一、冯诺依曼体系结构&#xff08;硬件层面&#xff09; 1.冯诺依曼体系结构 我们常见的…

容器核心技术-Namespace

一、容器 基于Linux 内核的 Cgroup&#xff0c; Namespace&#xff0c;以及Union FS等技术&#xff0c;对进程进行封装隔离&#xff0c;属于操作系统层面的虚拟化技术&#xff0c;由于隔离的进程独立于宿主和其它的隔离的进程&#xff0c;因此也称其为容器。 1.1 容器主要特性…

linux写文件如何保证落盘?

3.1.1. sync sync函数只是将所有修改过的块缓冲区排入写队列&#xff0c;然后就返回&#xff0c;它并不等待实际写磁盘操作结束。通常称为 update的系统守护进程会周期性地&#xff08;一般每隔30秒&#xff09;调用sync函数。这就保证了定期冲洗内核的块缓冲区。命令 sync也…

Linux 编译链接那些事儿(02)C++链接库std::__cxx11::basic_string和std::__1::basic_string链接问题总结

1 问题背景说明 在自己的项目源码中引用libeasysqlite.so时编译成功&#xff0c;但运行时遇到问题直接报错&#xff0c;找不到符号 symbol&#xff1a;_ZN3sql5FieldC1ENSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_10field_typeEi。 2 问题描述和解…

注意,注意,weak_ptr有坑

class Test { public:Test(){cout << "构造函数\n";}~Test(){cout << "析构函数\n";} }; void *operator new(size_t nsize) {void *ptmp std::malloc(nsize);printf("申请内存:%d,%p\n",nsize, ptmp);return ptmp; }void operator…