P4180 【模板】严格次小生成树[BJWC2010]

传送门

次小生成树

那肯定是最小生成树上改一条边(改两条肯定不如只改其中一条小)

那就枚举所有不在最小生成树上的边

考虑如果把此边加入,另一边删除后的情况

考虑要删哪条边后才能保持树的形态,并且总长最小

加入一条边后树就会出现一个环

那么删掉的边要在加入的边连接的两点间的路径上

并且删去的边要尽量大

那就可以用LCA来求出树上两点间路径上的最长边

但是现在还有一个问题,可能删去的边和加入的边一样长

所以还要维护一下次长的边

次长边维护也不难,具体看代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
inline int read()
{
    int x=0; char ch=getchar();
    while(ch<‘0‘||ch>‘9‘) ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘)
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x;
}

const int N=1e5+7;
const int M=3e5+7;
int n,m;
int fa[N];
long long s;
inline int find(int x){ return fa[x]==x ? x : fa[x]=find(fa[x]); }
struct edge
{
    int a,b,c;
}e[M];
inline bool cmp(const edge &a,const edge &b){ return a.c<b.c; }

vector <int> v[N],g[N];
bool p[M];
int f[N][27],dep[N];
long long mx[N][27],mxx[N][27];//mxx存次长边
void dfs(int x,int father)
{
    dep[x]=dep[father]+1; f[x][0]=father;
    for(int i=1;i<=20;i++)
    {
        f[x][i]=f[f[x][i-1]][i-1];
        mx[x][i]=max(mx[f[x][i-1]][i-1],mx[x][i-1]);
        mxx[x][i]=max(mxx[f[x][i-1]][i-1],mxx[x][i-1]);

        if(mx[ f[x][i-1] ][i-1]>mx[x][i-1])
            mxx[x][i]=max(mxx[x][i],mx[x][i-1]);
        if(mx[x][i-1]>mx[ f[x][i-1] ][i-1])
            mxx[x][i]=max(mxx[x][i],mx[ f[x][i-1] ][i-1]);
    }
    int len=v[x].size();
    for(int i=0;i<len;i++)
    {
        int u=v[x][i];
        if(u==father) continue;
        mx[u][0]=g[x][i]; mxx[u][0]=-1;
        dfs(u,x);
    }
}
inline long long LCA(int x,int y,int z)//询问最长边或者次长边
{
    long long res=-1e17+7;
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;i--)
        if(dep[f[x][i]]>=dep[y])
        {
            if(mx[x][i]!=z) res=max(res,mx[x][i]);
            else res=max(res,mxx[x][i]);
            x=f[x][i];
        }
    if(x==y) return res;
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i])
        {
            if(mx[x][i]!=z) res=max(res,mx[x][i]);
            else res=max(res,mxx[x][i]);
            if(mx[y][i]!=z) res=max(res,mx[y][i]);
            else res=max(res,mxx[y][i]);
            x=f[x][i]; y=f[y][i];
        }
    if(mx[x][0]!=z) res=max(res,mx[x][0]);
    else res=max(res,mxx[x][0]);
    if(mx[y][0]!=z) res=max(res,mx[y][0]);
    else res=max(res,mxx[y][0]);
    return res;
}

int main()
{
    n=read(); m=read();
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
        e[i].a=read(),e[i].b=read(),e[i].c=read();

    sort(e+1,e+m+1,cmp);
    int tot=0;
    for(int i=1;i<=m;i++)
    {
        int xa=find(e[i].a),xb=find(e[i].b);
        if(xa==xb) continue;
        s+=e[i].c; fa[xa]=xb;  p[i]=1;
        v[e[i].a].push_back(e[i].b); g[e[i].a].push_back(e[i].c);
        v[e[i].b].push_back(e[i].a); g[e[i].b].push_back(e[i].c);
        if(++tot==n-1) break;
    }//先求出最小生成树

    dfs(1,1);
    long long ans=1e17+7;
    for(int i=1;i<=m;i++)
    {
        if(p[i]) continue;//枚举所有没有加入最小生成树的边
        long long t=LCA(e[i].a,e[i].b,e[i].c);//找出最长边或者次长边
        if(t==e[i].c) continue;
        ans=min(ans,s-t+e[i].c);
    }
    cout<<ans;
    return 0;
}

原文地址:https://www.cnblogs.com/LLTYYC/p/9695527.html

时间: 2024-08-01 11:53:45

P4180 【模板】严格次小生成树[BJWC2010]的相关文章

Luogu P4180 【模板】严格次小生成树[BJWC2010]

P4180 [模板]严格次小生成树[BJWC2010] 题意 题目描述 小\(C\)最近学了很多最小生成树的算法,\(Prim\)算法.\(Kurskal\)算法.消圈算法等等.正当小\(C\)洋洋得意之时,小\(P\)又来泼小\(C\)冷水了.小\(P\)说,让小\(C\)求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是\(E_M\),严格次小生成树选择的边集是\(E_S\),那么需要满足:(\(value(e)\)表示边\(e\)的权值)\

洛谷 P4180 【模板】严格次小生成树[BJWC2010]【次小生成树】

严格次小生成树模板 算法流程: 先用克鲁斯卡尔求最小生成树,然后给这个最小生成树树剖一下,维护边权转点权,维护最大值和严格次大值. 然后枚举没有被选入最小生成树的边,在最小生成树上查一下这条边的两端点的路径上的最长边,如果最长边等于枚举到的边的边权,那么选次长边(没有次长边的话直接跳过),然后在最小生成树的权值上减去路径上最/次长边,加上当前枚举的边的边权 因为如果加入枚举的边的,那么就形成了一个环,需要断开一条边 注意一开始单点次小值赋为0 #include<iostream> #inclu

P4180 【模板】严格次小生成树[BJWC2010](严格次小生成树)

题目链接 题意如题 做法 先做一遍最小生成树 枚举添加每一条非树边的情况,每一次构成一棵基环树,在环上找一条最长边(如果等于该非树边就用环上的严格次小边) 倍增LCA,倍增预处理的时候顺便维护严格次大值和最大值(注意细节) (如果是非严格次小生成树则只需要维护最大值即可) 代码 #include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <algo

P4180 严格次小生成树[BJWC2010]

题目链接 https://www.luogu.org/problemnew/show/P4180 题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) \sum_{e \in E_M}value(e)<\sum_{e

P4180 严格次小生成树[BJWC2010] Kruskal,倍增

题目链接\(Click\) \(Here\). 题意就是要求一个图的严格次小生成树.以前被题面吓到了没敢做,写了一下发现并不难. 既然要考虑次小我们就先考虑最小.可以感性理解到一定有一种次小生成树,可以由最小生成树删一条边再加一条边得到.我们枚举加上去的这一条边,加上去以后原\(mst\)会成为一个基环树,想让它次小就在这个环里找一条最长的边(不包含新加进去的)删掉就好.放在树上来讲,就是找到\(u\)到\(v\)路径上的最大值.这样我们就有了非严格次小生成树. 严格要怎么处理?我们需要排除新加

洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

洛谷题目传送门 %%%天平巨佬和山楠巨佬%%% 他们的题解 思路分析 具体思路都在两位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 首先kruskal把最小生成树弄出来,因为要求次小生成树.至于为什么次小一定只在最小的基础上改变了一条边,我也不会证......打表找规律大法好 剩下的可以有一堆数据结构来维护最大值和次大值(原理两位巨佬都讲清楚了,这里只分析一下算法的优劣) 倍增+LCA 山楠巨佬的做法,我也写了这一种.复杂度\(O(MlogM(kruscal)+MlogN(

cogs P1578【模板】 次小生成树初级练习题

1578. 次小生成树初级练习题 ☆   输入文件:mst2.in   输出文件:mst2.out   简单对比时间限制:1 s   内存限制:256 MB [题目描述] 求严格次小生成树 [输入格式] 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z. [输出格式] 包含一行,仅一个数,表示严格次小生成树的边权和.(数据保证必定存在严格次小生成树) [样例输入] 5 6 1 2 1 1 3 2 2 4 3

洛谷P4180 [Beijing2010组队]次小生成树Tree

题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) \sum_{e \in E_M}value(e)<\sum_{e \in E_S}value(e)∑e∈EM??value(e)<∑e∈ES??value(e)

【模板】 次小生成树

1 /*for(遍历图中所有不属于最小生成树的边) 2 把当前边加入最小生成树产生回路 3 去掉回路中除当前边之外权值最大的边 4 记录下现在的树及其总权值 5 在上面循环产生的树中选一棵总权值最小的,就是次小生成树*/ 6 /*上面做法只遍历一次最小生成树,但是如果枚举删去最小生成树上的边,那就要求n-2次最小生成树了,所以不可取*/ 7 #include <cstdio> 8 #include <cstring> 9 #include <algorithm> 10