【总结】 (严格)次小生成树

前言

首先需要了解什么是最小生成树,还要知道什么是倍增(求Lca).

上面的东西如果了解了,就可以开始进入学习的路途了!!

1 算法框架

1.1 整体思路

用不是最小生成树上的边去更新答案.

1.2 具体维护

对于每一个倍增跳上去的,要维护两个东西:

  • 路径的边权最大值.
  • 路径的边权次大值

2 具体实现

我们考虑一下对于每一条边(不在最小生成树上),如果要把它加入答案,如何更新?

MST-路径最大值+边权.

然后这个东西就可以很愉快地解决了...

其实不是的,如果题目求的是次小生成树,这题就没了,但是题目要求的是严格次小生成树,怎么办呢?

考虑一下,如果要这样子,我们更新答案就不能和MST相等,然后依旧可以很愉快地解决了!!!

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define int long long
const int N=100010,M=300010,Inf=2e18+10;
struct node{
    int u,v,w;
}e[M];
int fa[N],front[N],to[N<<1],nxt[N<<1],w[N<<1],MST,cnt,n,m,k,flag[M];
int f[N][20],Min[N][20],Max[N][20],dep[N];
int find(int x){
    if(fa[x]!=x)fa[x]=find(fa[x]);
    return fa[x];
}
void Add(int u,int v,int W){
    to[++cnt]=v;nxt[cnt]=front[u];w[cnt]=W;
    front[u]=cnt;
}
bool cmp(node a,node b){
    return a.w<b.w;
}
void dfs(int u,int Fa){
    f[u][0]=Fa;
    for(int i=front[u];i;i=nxt[i]){
        int v=to[i];
        if(v!=Fa){
            dep[v]=dep[u]+1ll;
            Max[v][0]=w[i];
            Min[v][0]=-Inf;
            dfs(v,u);
        }
    }
}
int LCA(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    for(int i=18;~i;i--)
        if(dep[f[u][i]]>=dep[v])
            u=f[u][i];
    if(u==v)return u;
    for(int i=18;~i;i--)
        if(f[u][i]!=f[v][i])
            u=f[u][i],v=f[v][i];
    return f[u][0];
}
int qmax(int u,int v,int W){
    int ans=-Inf;
    for(int i=18;~i;i--)
        if(dep[f[u][i]]>=dep[v]){
            if(W!=Max[u][i])ans=max(ans,Max[u][i]);
            else ans=max(ans,Min[u][i]);
            u=f[u][i];
        }
    return ans;
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++){
        int u,v,W;scanf("%lld%lld%lld",&u,&v,&W);
        e[i]=(node){u,v,W};
    }
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=m;i++){
        int u=find(e[i].u),v=find(e[i].v);
        if(u!=v){
            flag[i]=1;
            Add(e[i].u,e[i].v,e[i].w);
            Add(e[i].v,e[i].u,e[i].w);
            fa[v]=u;MST+=e[i].w;
            k++;if(k==n-1)break;
        }
    }
    Min[1][0]=-Inf;
    dep[1]=1;
    dfs(1,-1);
    for(int j=1;j<=18;j++)
        for(int i=1;i<=n;i++){
            f[i][j]=f[f[i][j-1]][j-1];
            Max[i][j]=max(Max[i][j-1],Max[f[i][j-1]][j-1]);
            Min[i][j]=max(Min[i][j-1],Min[f[i][j-1]][j-1]);
            if(Max[i][j-1]>Max[f[i][j-1]][j-1])
                Min[i][j]=max(Min[i][j],Max[f[i][j-1]][j-1]);
            else if(Max[i][j-1]<Max[f[i][j-1]][j-1])
                Min[i][j]=max(Min[i][j],Max[i][j-1]);
        }
    int ans=Inf;
    for(int i=1;i<=m;i++)
        if(!flag[i]){
            int u=e[i].u,v=e[i].v;
            int lca=LCA(u,v);
            int Maxu=qmax(u,lca,e[i].w),Maxv=qmax(v,lca,e[i].w);
            ans=min(ans,MST-max(Maxu,Maxv)+e[i].w);
        }
    printf("%lld\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/biscuit46/p/9880303.html

时间: 2024-08-30 10:59:53

【总结】 (严格)次小生成树的相关文章

HDU4081 Qin Shi Huang&#39;s National Road System【Kruska】【次小生成树】

Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3979    Accepted Submission(s): 1372 Problem Description During the Warring States Period of ancient China(4

Ural 1416 Confidential,次小生成树

不严格次小生成树. 注意图可能不连通. #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int maxn = 505; const int INF = 1e7; bool vis[maxn]; int d[maxn]; int pre[maxn]; int Max[maxn][maxn]; int g[

TOJ--1278--最小生成树

今天中午做的 第一次用邻接表去实现... 我就写了下prim的 相比于kruskal 还是更喜欢它多一点... 虽然知道prim+heap优化 可是我写不来..... 对于 heap 虽然觉得它的概念很简单 但实现起来真的好伤啊.. 我想 对于prim的理解应该差不多了 基本上可以直接手码出来了 虽然这个很简单.... 以前原来就有一篇 prim的介绍 那我就懒的写了 直接上代码吧  一般都是用  邻接矩阵实现的.. #include <iostream> #include <cstri

次小生成树(SST)

次小生成树(SST) 题目背景 Awson是某国际学校信竞组的一只菜鸡.Awson最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当Awson洋洋得意之时,信竞组其他大佬又来泼Awson冷水了. 题目描述 他们说,让Awson求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:              (value(e) 表示边 e的权值)这下Awson蒙了,他

poj1679+次小生成树

Description Given a connected undirected graph, tell if its minimum spanning tree is unique. Definition 1 (Spanning Tree): Consider a connected, undirected graph G = (V, E). A spanning tree of G is a subgraph of G, say T = (V', E'), with the followin

HDU4081 Qin Shi Huang&#39;s National Road System(次小生成树)

枚举作为magic road的边,然后求出A/B. A/B得在大概O(1)的时间复杂度求出,关键是B,B是包含magic road的最小生成树. 这么求得: 先在原图求MST,边总和记为s,顺便求出MST上任意两点路径上的最长边d[i][j]. 当(u,v)是magic road时, 如果它在原本的MST上,则B就等于s-原(u,v)的权,而原(u,v)的权其实就是d[u][v]: 如果它不在原本的MST上,则B就等于s-d[u][v]+0. 总之就是一个式子:B=s-d[u][v]. 于是,在

[POJ1679]The Unique MST 次小生成树

题目链接:http://poj.org/problem?id=1679 给你一个图的连通情况,询问你此图的最小生成树是否唯一. 假如最小生成树唯一,即生成树连通所有节点的权值和唯一.假如不唯一,那么存在另一条最小生成树使得权值等于之前最小生成树的权值. 换个思路考虑,也就是次小生成树的权值与最小生成树的权值相同,那么问题就变成了求次小生成树的权值. 我选择的是先求出最小生成树,将树上用到的边都保存下来.接着分别将每一条用到的边摘下来,再求一次最小生成树.假如不包含当前删掉的边生成的生成树的所选边

poj1679The Unique MST判断最小生成树是否唯一以及求次小生成树边权和的讲解

Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 22346   Accepted: 7924 Description Given a connected undirected graph, tell if its minimum spanning tree is unique. Definition 1 (Spanning Tree): Consider a connected, undirected graph G =

次小生成树 最小度限制生成树

首先说明这是一个坑! 因为发现啊次小生成树为什不用树链剖分写(虽然麻烦但是思路各种清晰!),最小度限制生成树可以用lct写(而且是似乎要比那个直接写的算法容易因为要各种建边删边dfs(就没有考虑过时间么)!!!!)!!!(好像是错的) (因为是自己傻叉写不出来) (半小时后觉得还是自己傻叉了……下面那个代码应该继续敲就行了,一个是最小生成树还没快排,然后在求最小度限制生成树时用邻接矩阵比较简单(反正时间复杂度也是o(n²)),然后每次就直接dfs一遍,dfs的时候传两个参,一个是这个节点,一个是

BZOJ1977 [BeiJing2010组队]次小生成树 Tree

恩,归类上来讲的话...是一道非常好的noip题...只不过嘛...(此处省略100字) 然后将如何做: 首先Kruskal求出最小生成树. 我们其实可以发现严格的次小生成树不可能在MST上改两条边 <=> 只能改一条边. 那么如何改呢? 每次在MST中加入一条非树边,即不在MST的边,那么会形成一个环,只要找到换上的严格小于当前边权的最大值,删之,就形成了次小生成树的候选. 由Kruskal的算法保证加入的边权一定是环上最大的,因此我们要记录树链上的最大值和次大值(因为是严格小于) 而记录的