生成树的概念:

在一个无向图中,设顶点数为\(n\),取其中\(n-1\)条边并使所有点相连,所得到的一棵树即为生成树。

最小生成树:

如果还没有接触过生成树的同学,欢迎戳->最小生成树详解

次小生成树:

次小生成树顾名思义,就是边权之和次小的一棵生成树。有严格次小生成树与非严格次小生成树之分。

尽管这个算法的名字叫做次小生成树,不过其实可以理解成一道数据结构

次小生成树可以和次短路径一起理解,有兴趣的同学可以做做模板题

非严格次小生成树的边权之和不一定要比最小生成树的小,即\(Σw\)次≥\(Σw\)最

严格次小生成树的边权之和一定要比最小生成树的小,即\(Σw\)次>\(Σw\)最

想要求出次小生成树?

显然我们可以dfs求出所有的生成树,在排序选出严格次小的就可以了。

但是上述方法显然不能快速求出次小生成树

为了快速的求出来次小生成树,我们需要知道一个结论:次小生成树和最小生成树之间只有一条边的差异。

这一条结论为什么是正确的呢?

我们先来看看\(Kruskal\)是怎么求最小生成树的

\(Kruskal\)是按照边权排序,按照贪心的思路一条一条的加进树边,所以如果要求出次小生成树,那么我们可以放弃一条小的边不选,加入另一条边使图联通。

如果我们"放弃"了两条边,那么只"放弃"一条边的生成树的边权和显然小于"放弃"两条边的生成树的边权和。

所以求出(非)严格次小生成树的朴素算法为:先建立一棵最小生成树。再枚举删去最小生成树上的每一条路径,再在新构成的生成树中选出一棵边权之和最小生成树的即可。

时间复杂度为\(O(nmlogm)\)。当然这种算法还不够优秀,我们可以继续优化

非严格次小生成树我们可以这样优化:

枚举边的时候,枚举没有被并入到最小生成树的边(我们将把这条边加入到生成树中,显然现在的图形不再是一棵树,所以我们要原图中删去一条最大边,使新图仍然联通)

加入边权值为\(W1\)。查询树上每一条的路径,在路径选取一个边权最大值\(W2\)。则当前枚举的答案为\(W−W2+W1\)(W为最小生成树的边权之和)

枚举所有的边之后,取最小值即可。

我们可以用倍增/树剖/LCT来实现查询树上最大值的操作,故复杂度为:\(O(mlogn)\)

再来看看严格次小生成树

不难发现:求非严格最小生成树时,枚举一条边\(W1\),之后再寻找一条生成树上的最大边\(W2\)

显然\(W1≥W2\),因此可能由此得到的次小生成树并非严格。所以我们可以在每次查询时,需要找到严格次小的\(W1\),即\(W1>W2\),这样我们就可以得到严格次小生成树了。

维护仍可以用倍增或者树剖思想。

这里介绍树剖的思路:

先用线段树维护区间最小值与严格次小值,如果是叶子节点最大值就设为他本身,次大值设为0

合并的时候把两个区间的这四个值(两个最大值与两个次大值)排序,再寻找最大值与严格次小值即可。

代码如下:

#include

using namespace std;

#define re register

#define il inline

#define int long long //把所有int转成longlong

#define debug printf("Now is line %d\n",__LINE__);

il int read()

{

re int x=0,f=1;char c=getchar();

while(c'9'){if(c=='-') f=-1;c=getchar();}

while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();

return x*f;

}//快读

#define maxm 300005

#define inf 12345678900000000

#define maxn 100005

struct Edge{int u,v,w,next;}e[maxm<<1];

struct qj{int ma,ma2;}q[maxn<<2];

struct Edge1

{

int u,v,w;

bool operator

}edge[maxm];

int n,m,vis[maxm],ans=inf,head[maxn],cnt,fa[maxn],mtree;

il void add(int u,int v,int w)

{

e[++cnt].v=v;

e[cnt].w=w;

e[cnt].next=head[u];

head[u]=cnt;

}//前向星加边

namespace smallesttree

{

il int find(int x)

{

while(x!=fa[x]) x=fa[x]=fa[fa[x]];

return x;

}//并查集找祖先

il void init()

{

for(re int i=1;i<=n;i++) fa[i]=i; //预处理并查集

for(re int i=0;i

}

il void kruskal()

{

init();

sort(edge,edge+m);

re int T=0;

for(re int i=0;i

{

re int eu=find(edge[i].u),ev=find(edge[i].v);//寻找祖先

if(eu!=ev)

{

add(edge[i].u,edge[i].v,edge[i].w),add(edge[i].v,edge[i].u,edge[i].w);

mtree+=edge[i].w;//记录子树大小

fa[ev]=eu;//合并

vis[i]=1;//标记该边为树边

if(++T==n-1) break;//边数等于节点数+1即为一颗树

}

}

}

}

//求出最小生成树

namespace treecut

{

int dep[maxn],father[maxn],top[maxn],W[maxn],a[maxn],size[maxn],son[maxn],seg[maxn],col;

//dep:深度 father:父亲节点 top:重链的顶端 W:到根节点的距离 a:点的权值 size:子树大小 son:重儿子 seg:在线段树中的序号(dfs序)

il void dfs1(int u,int fr)

{

dep[u]=dep[fr]+1;

size[u]=1;

father[u]=fr;

for(re int i=head[u];i;i=e[i].next)

{

re int v=e[i].v;

if(v!=fr)

{

W[v]=W[u]+e[i].w;//W为每一个点到根节点的距离

dfs1(v,u);

size[u]+=size[v];

if(size[v]>size[son[u]]) son[u]=v;

}

}

}//预处理出dep、size、father以及son

il void dfs2(int now,int fi)

{

top[now]=fi;

seg[now]=++col;

a[col]=W[now]-W[father[now]];//a为点的权值(它与之父亲节点边的权值)(相当于前缀和)

if(!son[now]) return;

dfs2(son[now],fi);

for(re int i=head[now];i;i=e[i].next)

{

re int v=e[i].v;

if(v!=son[now]&&v!=father[now]) dfs2(v,v);

}

}//预处理出每个节点的top、seg以及权值

//树剖模板就不解释了

#define ls k<<1

#define rs k<<1|1

il bool CMP(int a,int b){return a>b;}

il int getse(int x,int g,int z,int c)

{

re int a[5]={x,g,z,c};

sort(a,a+4,CMP);

for(re int i=1;i<3;++i)

{

if(a[i]!=a[0]) return a[i];

}

}//找到两个区间的最大值和严格次大值(四个数)的最大值与严格次大值

// 就是合并两个区间的最大值和严格次大值

il void build(int k,int l,int r)

{

if(l==r)

{

q[k].ma=a[l];

return;

}

re int mid=(l+r)>>1;

build(ls,l,mid),build(rs,mid+1,r);

q[k].ma=max(q[ls].ma,q[rs].ma);

q[k].ma2=getse(q[ls].ma,q[rs].ma,q[ls].ma2,q[rs].ma2);

}//预处理出区间最大值与次大值

il qj query(int k,int l,int r,int ll,int rr)

{

if(ll>r||rr

if(ll<=l&&rr>=r) return (qj){q[k].ma,q[k].ma2};

re int mid=(l+r)>>1;

re qj t1=query(ls,l,mid,ll,rr),t2=query(rs,mid+1,r,ll,rr);

return (qj){max(t1.ma,t2.ma),getse(t1.ma,t2.ma,t1.ma2,t2.ma2)};

}//查询区间的区间的最大值与次小值

il int LCA(int u,int v,int d)

{

re int need=-inf;

while(top[u]!=top[v])

{

if(dep[top[u]]

qj temp=query(1,1,n,seg[top[u]],seg[u]);

u=father[top[u]];

need=max(need,(temp.ma==d)?temp.ma2:temp.ma);//严格次小边(如果temp.ma==k就是非严格次小)

}

if(dep[u]

qj temp=query(1,1,n,seg[v]+1,seg[u]);

return max(need,(temp.ma==d)?temp.ma2:temp.ma);//同上

}

il void init()

{

dfs1(1,0),dfs2(1,1),build(1,1,n);

}

}

//树链剖分

signed main()

{

n=read(),m=read();

smallesttree::kruskal();//求出最小生成树

treecut::init();//预处理

for(re int i=0;i

{

if(vis[i]) continue;//枚举所有非树边(没有在最小生成树的边)

re int temp=mtree/*最小生成树边权和*/+edge[i].w/*本来的树边的边权*/-treecut::LCA(edge[i].u,edge[i].v,edge[i].w)/*找到严格次小边的边权*/;

if(ans>temp&&temp!=mtree+e[i].w/*其实就是严格此小边不为0(没有找到严格次小边)*/&&temp>mtree) ans=temp;

}

printf("%lld",ans);

return 0;

}

一棵树的生成树有几颗_次小生成树(树剖,生成树)相关推荐

  1. 智慧树python数据分析与数据可视化_知到APP智慧树Python数据分析与数据可视化慕课答案...

    两个数相除,商是5,除数是20,被除数最大是______. "班级"一词最早的提出人是().A.赫尔巴特B.夸美纽斯C.埃拉斯莫斯D. 下列哪些项属于我国城市规划编制体系的范畴() ...

  2. 树(5)-----判断两颗树一样或者一棵树是否是另外一颗的子树

    1.判断两颗树是否一样.(递归) def isSameTree(p,q):if not p and not q:return Trueelif not p and q or (not q and p) ...

  3. 在一颗度为4的树T中,若有20个度为4的结点,10个度为3的结点,1个度为2的结点,10个度为1的结点,则树T的叶结点个数是( )

    在一颗度为4的树T中,若有20个度为4的结点,10个度为3的结点,1个度为2的结点,10个度为1的结点,则树T的叶结点个数是( ) A. 41 B. 82 C. 113 D. 122 设树中度为i(i ...

  4. 室内主题元素分析图_主题乐园包装——“树”造型案例精选分享

    点击蓝字 关注我们 "树"主题项目 树文化主题园区是围绕着和树木有关的故事为主题,以树木为造型的主题娱乐区,为人们提供集娱乐活动.观光体验.休闲游憩于一体的主题乐园.如迪士尼奥兰多 ...

  5. 树 | 突然间,看了这篇文章,树我懂了!

    Hello ! 我是小小,今天总结一下什么是树,以及关于树的一些内容.. 树 树是一种非常常用的数据结构,与线性表,堆栈并驾齐驱. 树的定义 树是从自然界抽象出来的,它指的是N个父子节点的有限集合,对 ...

  6. eNSP第三篇:STP,生成树,xSTP,MSTP,多生成树,交换机工作原理,环路的形成

    STP,生成树,xSTP,MSTP,多生成树,交换机工作原理,环路的形成 了解环路的形成 交换机的工作原理 交换机接口在接收到数据包时,会检查数据包的源MAC地址和目的MAC地址,然后查询MAC地址表 ...

  7. 哈夫曼编码(基于哈夫曼树-最优二叉树,不唯一)、B树(b-树)、B+树

    整合自: http://blog.csdn.net/shuangde800/article/details/7341289 http://www.cnblogs.com/Jezze/archive/2 ...

  8. l2-004 这是二叉搜索树吗? (25分)_什么是 “线段树” ?

    线段树是一个复杂的数据结构,比较难理解,也比较难解释清楚.在我将这个数据结构反复学习了五遍的时候,我终于有了信心写出这篇介绍线段树的文章.希望大家能够掌握这种数据结构. 这篇文章比较长,建议大家耐心阅 ...

  9. 目录树 删除 数据结构_数据结构:B树和B+树的插入、删除图文详解

    B树 1.1B树的定义 B树也称B-树,它是一颗多路平衡查找树.我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数.当m取2时,就是我们常见的二叉搜索树. ...

最新文章

  1. linux c 启动程序吗,Linux下C程序启动时的系统调用
  2. 用PHP做负载均衡指南
  3. javaSE各阶段练习题--面向对象-static-String-StringBuilder
  4. 使用 Avalonia 开发 UOS 原生应用
  5. 了解自定义对象创建:JSON绑定概述系列
  6. oracle如何检查是否rac,Oracle RAC 状态检查
  7. oracle dg物理和逻辑,Oracle DG介绍(物理无实例)
  8. 【C++自我精讲】基础系列三 重载
  9. Google的特殊功能
  10. R语言:时间序列ARIMA模型使用
  11. matlab ode45求解齿轮动力学,Matlab讨论区 - 声振论坛 - 振动,动力学,声学,信号处理,故障诊断 - Powered by Discuz!...
  12. 如何在excel中单独冻结多行或多列
  13. 不用命令行WinRAR解压7z.001格式的文件
  14. 二维平面上线段与直线位置关系的判定
  15. Android下载网上图片
  16. Android Studio 导出的apk安装时出现解析软件包错误(已解决)
  17. 第一周预习HTML标签(笔记可以不记,代码一定要敲)
  18. 通配符及反掩码的详解 (网络中ACL )
  19. python3 爬虫相关学习7:使用 BeautifulSoup下载网页图片到本地文件夹
  20. 通用互联网应用架构图

热门文章

  1. mysql主从不同步怎么恢复_mysql主从不同步时,怎么恢复
  2. 华北电力大学保定校区计算机专业,华北电力大学保定校区本科计算机科学与技术_华北电力大学保定校区本科计算机科学与技术简介-查字典学校网...
  3. 电脑html按键侧滑广告,HTML5侧滑聊天面板
  4. MVC--Razor(2)
  5. 轻量级网络skipnet
  6. linux RTX2080显卡驱动
  7. from torch._C import * ImportError: DLL load failed 动态链接库(DLL)初始化例程失败
  8. python检测网格
  9. numpy基本矩阵操作
  10. OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑