非严格/严格次小生成树问题

转载自新博客:https://acxblog.site/archives/smst-problem.html

Problem

BZOJ 入门OJ P1634

Luogu P4180 BJWC 次小生成树

Introduction

首先讲非严格次小生成树的做法。

先建立权值之和为\(W\)的最小生成树。接着枚举没有被并入到MST的边(u,v)(我们将把这条边加入到MST中,并在原MST中删去一条最大边,使新ST仍然联通),权值为\(W_e\)。查询树上u->v的路径,在路径选取一个边权最大值\(W_m\)。则当前枚举的答案为\(W-W_m+W_e\)。枚举所有的边之后,取最小值即可。

基本无需证明。这个代码就不给了,可以用倍增和树剖等实现。

接下来讲解严格次小生成树的做法。

原方法是枚举一条边\(W_e\),之后再寻找一条MST上的合法最大边(即在路径上)\(W_m\)。显然\(W_e \geq W_m\),因此可能由此得到的次小生成树并非严格。所以我们可以再查找路径上的严格次小值\(W_{m2}\),则显然\(W_e > W_{m2}\),所以由此得到的生成树一定严格小于MST。同样枚举所有的边,取最小值即可。

顺便一提,我这题错的地方忘记加了这个,跳点的倍增数组:

gup[i][j]=gup[gup[i][j-1]][j-1];

MMP

以及,两种算法的时间复杂度皆为:\(O(m\ logn)\)

补充:合并严格最大值和次大值

假设合并的两对最大值/次大值分别为\(m_1,s_1,m_2,s_2\),新的最大值/次大值为\(m,s\)。

我们可以证明,\(m\)一定会在\(m_1,m_2\)决出,则直接在两个里取个最大值就行。

然后\(s\)首先在\(s_1,s_2\)取最大值,若\(m_1\)不等于\(m_2\),再与\(min(m_1,m_2)\)进行比对。

Code

// Code by ajcxsu
// Problem: Second Minium Spanning Tree

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

typedef long long ll;
const int N=1e6+10, M=3e6+10;
struct Edge {
    int u,v;
    ll w;
    bool operator < (const Edge &b) { return w<b.w; }
} e[M];
bool S[M]; // 标记MST
int h[N],to[M],nexp[M];
ll W[M];
int p=1;
inline void ins(int a, int b, int w) {
    nexp[p]=h[a], h[a]=p, to[p]=b, W[p]=w, p++;
}

int fa[N];
int Find(int x) {
    if(!fa[x]) return x;
    return fa[x]=Find(fa[x]);
}
bool Union(int a, int b) {
    int af=Find(a), bf=Find(b);
    if(af==bf) return false;
    fa[af]=bf;
    return true;
}

const int OP=20;
int dep[N], gup[N][OP], ma[N][OP], sma[N][OP];
void dfs(int x, int k) {
    dep[x]=k;
    for(int u=h[x];u;u=nexp[u])
        if(!dep[to[u]]) {
            gup[to[u]][0]=x;
            ma[to[u]][0]=W[u];
            sma[to[u]][0]=-1;
            dfs(to[u], k+1);
        }
}

ll m1,m2;
inline void upd(ll x) {
    if(x>m1) m2=m1, m1=x; // m1的值一定要移到m2
    else if(x<m1 && x>m2) m2=x;
}
void lca(int a,int b) {
    m1=m2=0;
    if(dep[a]<dep[b]) swap(a,b);
    for(int j=OP-1;j>=0;j--)
        if(dep[gup[a][j]]>=dep[b]) upd(ma[a][j]), upd(sma[a][j]), a=gup[a][j];
    if(a==b) return;
    for(int j=OP-1;j>=0;j--)
        if(gup[a][j]!=gup[b][j]) {
            upd(ma[a][j]), upd(sma[a][j]);
            upd(ma[b][j]), upd(sma[b][j]);
            a=gup[a][j], b=gup[b][j];
        }
    int j=0;
    upd(ma[a][j]), upd(sma[a][j]);
    upd(ma[b][j]), upd(sma[b][j]);
}

template <typename T> void gn(T &x) {
    x=0;
    char ch=getchar();
    while(ch<‘0‘ || ch>‘9‘) ch=getchar();
    while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘, ch=getchar();
}

int main() {
    int n,m;
    gn(n), gn(m);
    for(int i=0;i<m;i++)
        gn(e[i].u), gn(e[i].v), gn(e[i].w);
    sort(e,e+m);
    ll mst=0;
    for(int i=0, cnt=0;i<m && cnt!=n-1;i++) {
        if(Union(e[i].u, e[i].v)) {
            S[i]=1, mst+=e[i].w;
            ins(e[i].u, e[i].v, e[i].w), ins(e[i].v, e[i].u, e[i].w);
            cnt++;
        }
    }
    dfs(1,1);
    int a[4],ee,ff;
    for(int j=1;j<OP;j++)
        for(int i=1;i<=n;i++) {
            gup[i][j]=gup[gup[i][j-1]][j-1];
            a[0]=ma[i][j-1], a[1]=ma[gup[i][j-1]][j-1];
            a[2]=sma[i][j-1], a[3]=sma[gup[i][j-1]][j-1];
            ee=max(a[0],a[1]), ff=max(a[2],a[3]);
            if(a[0]!=a[1]) ff=max(ff, min(a[0],a[1]));
            ma[i][j]=ee, sma[i][j]=ff;
        }

    ll ans=0x7ffffffffffffll;
    for(int i=0;i<m;i++)
        if(!S[i]) {
            lca(e[i].u, e[i].v);
            if(m1!=e[i].w) ans=min(ans, -m1+e[i].w);
            else ans=min(ans, -m2+e[i].w);
        }
    printf("%lld\n",mst+ans);
    return 0;
}

原文地址:https://www.cnblogs.com/acxblog/p/8947292.html

时间: 2024-10-15 06:29:04

非严格/严格次小生成树问题的相关文章

次短路径与次小生成树问题的简单解法——转自:BYVoid

次短路径与次小生成树问题的简单解法 [次短路径] 次短路径可以看作是k短路径问题的一种特殊情况,求k短路径有Yen算法等较为复杂的方法,对于次短路径,可以有更为简易的方法.下面介绍一种求两个顶点之间次短路径的解法. 我们要对一个有向赋权图(无向图每条边可以看作两条相反的有向边)的顶点S到T之间求次短路径,首先应求出S的单源最短路径.遍历有向图,标记出可以在最短路径上的边,加入集合K.然后枚举删除集合K中每条边,求从S到T的最短路径,记录每次求出的路径长度值,其最小值就是次短路径的长度. 在这里我

同步/异步,阻塞/非阻塞的个人小总结

同步/异步,阻塞/非阻塞的个人小总结 最近一直在用scrapy写编写爬虫程序,在阅读其源码和开源项目的时候,常常会接触到一些网络编程相关的东东. 首先,Scrapy是由Twisted写的一个受欢迎的Python事件驱动网络框架, 它使用的是非堵塞的异步处理. 在网络编程中有 阻塞/非阻塞 和 同步/异步的概念,这里我做了一个总结: 因为中文语意的问题,很多时候确实会导致混用,而且语境不一样意义也可能不一样.在这里 网络编程严重区别与线程控制. 线程控制中: 阻塞非阻塞:可以简单理解为需要做一件事

任正非说华为是小草,要是小草被踩死了,华为人只有哭的份

当地时间1月22日达沃斯论坛上,华为创始人.总裁任正非 首次公开演讲,接受与BBC主播的对话. 看了一下,觉得任正非虽为华为总裁,但是总觉得说法不靠谱. 他说: 我认为美国在电子信息技术上,过去是绝对的强势,而且未来几十年,美国还会是相对的优势,华为这个小草不可能改变时代列车的轨道,但是我们小草在努力成长,当然我们也希望把自己脱胎换骨,从草变成小树苗. 虽然理解为谦虚,但是说华为这个小草不可能改变时代列车的轨道,就不靠谱,因为华为已经是通信巨头,年营业额560亿美元.我觉得他说这话像是10几年前

POJ2831(次小生成树问题)

Can We Build This One? Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 1475   Accepted: 546 Case Time Limit: 2000MS Description “Highways are built, then life is rich.” Now people of Big Town want to become rich, so they are planning to

PC端一些非经典兼容性问题小札

IE10默认在input框中输入内容后会显示一个‘X’按钮,方便删除输入的所有内容. 在password输入框显示一个眼睛的按钮,去掉叉叉和眼睛的方法如下: ::-ms-clear { display: none; } ::-ms-reveal { display: none; } 使用方法举例: input[type="text"]::-ms-clear { display: none; }

Postgresql 远程同步(非实时同步,小数据量)

源端要开通目标的相关访问权限 目标端: 1.建立远程表的视图 create view v_bill_tbl_version_update_control_info as SELECT * FROM dblink('hostaddr=10.10.10.8 port=4321 dbname=postgres user=postgres password=postgres', 'SELECT id,appid,ratio,status,create_time,char_package_name,ver

次短路径与次小生成树问题的简单解法

[次短路径] 次短路径可以看作是k短路径问题的一种特殊情况,求k短路径有Yen算法等较为复杂的方法,对于次短路径,可以有更为简易的方法.下面介绍一种求两个顶点之间次短路径的解法. 我们要对一个有向赋权图(无向图每条边可以看作两条相反的有向边)的顶点S到T之间求次短路径,首先应求出S的单源最短路径.遍历有向图,标记出可以在最短路径上的边,加入集合K.然后枚举删除集合K中每条边,求从S到T的最短路径,记录每次求出的路径长度值,其最小值就是次短路径的长度. 在这里我们以为次短路径长度可以等于最短路径长

小非大人说·华为云——弹性云服务器ECS

小非大人说·华为云--弹性云服务器ECS小非大人:工程师小闲大人:售前小年大人:老板 昨天听 小闲大人 说,国内公有云市场的份额已经基本稳固,于是小非大人一惊,这就稳固啦,那我赶紧开始学习吧. 第一章(想标题中..) 小年大人 咳嗽一声,既然大家都说完了,那我说两句,给新来的同学就公有云梳理下框架,首先回到10年前,VMware,Citrix,微软的产品,VIEW,VSPHERE,XEN DESKTOP,Hyper-V,这些都是私有云的产品,经过多年深耕,已经在各行各业广泛应用,但近几年,可以看

小非大人说·华为云——弹性负载均衡器ELB

小非大人说·华为云--弹性负载均衡器ELB小非大人:工程师小闲大人:售前小年大人:老板 小闲大人说,熊迪,一客户说今天要配置弹性云负载均衡器ELB,去支持下,熊迪. 其实我想和小闲大人说,可不可以不做你的熊迪,吃饭又不叫我,算了,不吃饭, 补下负载均衡内容,亲们一提起负载均衡,要么Citrix netscalar,F5-BIG-LTM/GTM等大佬产品,那么华为云的ELB和它们功能一样,优点是稳定,简单,易上手,容易等.先上拓扑图为敬 第一部分-配置选购第二部分-实践场景第三部分-价格说明备注: