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

严格次小生成树模板

算法流程:

先用克鲁斯卡尔求最小生成树,然后给这个最小生成树树剖一下,维护边权转点权,维护最大值和严格次大值。

然后枚举没有被选入最小生成树的边,在最小生成树上查一下这条边的两端点的路径上的最长边,如果最长边等于枚举到的边的边权,那么选次长边(没有次长边的话直接跳过),然后在最小生成树的权值上减去路径上最/次长边,加上当前枚举的边的边权

因为如果加入枚举的边的,那么就形成了一个环,需要断开一条边

注意一开始单点次小值赋为0

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=300005;
int n,m,h[N],cnt,f[N],con,fa[N],si[N],hs[N],de[N],fr[N],id[N],rl[N],va[N],tmp;
long long ans=1e18,sum;
bool mk[N];
struct qwe
{
    int ne,to,va;
}e[N<<1];
struct xds
{
    int l,r,mx,cmx;
}t[N<<1];
struct bian
{
    int u,v,w;
}a[N*3];
bool cmp(const bian &a,const bian &b)
{
    return a.w<b.w;
}
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>‘9‘||p<‘0‘)
    {
        if(p==‘-‘)
            f=-1;
        p=getchar();
    }
    while(p>=‘0‘&&p<=‘9‘)
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
inline int zhao(int x)
{
    return x==f[x]?x:f[x]=zhao(f[x]);
}
void add(int u,int v,int w)
{
    cnt++;
    e[cnt].ne=h[u];
    e[cnt].to=v;
    e[cnt].va=w;
    h[u]=cnt;
}
void dfs1(int u,int fat)
{
    fa[u]=fat;
    de[u]=de[fat]+1;
    si[u]=1;
    for(int i=h[u];i;i=e[i].ne)
        if(e[i].to!=fat)
        {
            va[e[i].to]=e[i].va;
            dfs1(e[i].to,u);
            si[u]+=si[e[i].to];
            if(si[e[i].to]>si[hs[u]])
                hs[u]=e[i].to;
        }
}
void dfs2(int u,int top)
{
    fr[u]=top;
    id[u]=++tmp;
    rl[tmp]=u;
    if(!hs[u])
        return;
    dfs2(hs[u],top);
    for(int i=h[u];i;i=e[i].ne)
        if(e[i].to!=hs[u]&&e[i].to!=fa[u])
            dfs2(e[i].to,e[i].to);
}
void build(int ro,int l,int r)
{
    t[ro].l=l,t[ro].r=r;
    if(l==r)
    {
        t[ro].mx=va[rl[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(ro<<1,l,mid);
    build(ro<<1|1,mid+1,r);
    t[ro].mx=max(t[ro<<1].mx,t[ro<<1|1].mx);
    if(t[ro<<1].mx==t[ro<<1|1].mx)
            t[ro].cmx=max(t[ro<<1].cmx,t[ro<<1|1].cmx);
    else
        t[ro].cmx=min(t[ro<<1].mx,t[ro<<1|1].mx);
}
int ques(int ro,int l,int r,int w)
{
    if(t[ro].l==l&&t[ro].r==r)
        return t[ro].mx==w?t[ro].cmx:t[ro].mx;
    int mid=(t[ro].l+t[ro].r)>>1;
    if(r<=mid)
        return ques(ro<<1,l,r,w);
    else if(l>mid)
        return ques(ro<<1|1,l,r,w);
    else
        return max(ques(ro<<1,l,mid,w),ques(ro<<1|1,mid+1,r,w));
}
int wen(int u,int v,int w)
{
    int re=0;
    while(fr[u]!=fr[v])
    {
        if(de[fr[u]]<de[fr[v]])
            swap(u,v);
        re=max(re,ques(1,id[fr[u]],id[u],w));
        u=fa[fr[u]];
    }
    if(u!=v)
    {
        if(de[u]>de[v])
            swap(u,v);
        re=max(re,ques(1,id[u]+1,id[v],w));
    }
    return re;
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
        a[i].u=read(),a[i].v=read(),a[i].w=read();
    sort(a+1,a+1+m,cmp);
    for(int i=1;i<=n;i++)
        f[i]=i;
    for(int i=1;i<=m&&con<n-1;i++)
    {
        int fu=zhao(a[i].u),fv=zhao(a[i].v);
        if(fu!=fv)
        {
            f[fu]=fv,con++,sum+=a[i].w;
            add(a[i].u,a[i].v,a[i].w),add(a[i].v,a[i].u,a[i].w);
            mk[i]=1;
        }
    }
    dfs1(1,0);
    dfs2(1,1);
    build(1,1,n);
    for(int i=1;i<=m;i++)
        if(!mk[i])
            ans=min(ans,sum-wen(a[i].u,a[i].v,a[i].w)+a[i].w);
    printf("%lld\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/lokiii/p/8971900.html

时间: 2024-10-09 09:46:49

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

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

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

【C++】最近公共祖先LCA(Tarjan离线算法)&amp;&amp; 洛谷P3379LCA模板

1.前言 首先我们介绍的算法是LCA问题中的离线算法-Tarjan算法,该算法采用DFS+并查集,再看此算法之前首先你得知道并查集(尽管我相信你如果知道这个的话肯定是知道并查集的),Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解(个人认为). 2.思想 下面详细介绍一下Tarjan算法的思想: 1.任选一个点为根节点,从根节点开始. 2.遍历该点u所有子节点v,并标记这些子节点v已被访问过. 3.若是v还有子节点,返回2,否则下一步. 4.合并v到u上. 5.寻找与当前点

AC自动机(附洛谷P3769模板题)

首先,介绍一下AC自动机(Aho-Corasick automaton),是一种在一个文本串中寻找每一个已给出的模式串的高效算法. 在学习AC自动机之前,你需要先学习Trie树和KMP算法,因为AC自动机正式利用并结合了两者的思想. 说到实际的不同,其实AC自动机只是在Trie树上引入了一个类似KMP中next数组的东西叫做Fail指针. 对于每一个节点,Fail指针指向该节点所代表的字符串中,次长的.在Trie树中存在的后缀(因为最长的在Trie树种存在的后缀就是其本身)所代表的节点. 举例:

洛谷P3375 [模板]KMP字符串匹配

To 洛谷.3375 KMP字符串匹配 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果你不知道这是什么意思也不要问,去百度搜[kmp算法]学习一下就知道了. 输入输出格式 输入格式: 第一行为一个字符串,即为s1(仅包含大写字母) 第二行为一个字符串,即为s2(仅包含大写字母) 输出格式: 若干行,每行包含一个整数,表示s2在s1中出现的位置 接下来1行,包括length(s2)个整

洛谷.3803.[模板]多项式乘法(FFT)

题目链接:洛谷.LOJ. FFT相关:快速傅里叶变换(FFT)详解.FFT总结.从多项式乘法到快速傅里叶变换. #include <cmath> #include <cctype> #include <cstdio> #include <algorithm> #define gc() getchar() const int N=1e6+5; const double PI=acos(-1); int n,m; struct Complex { double

洛谷.1919.[模板]A乘B Problem升级版(FFT)

题目链接:洛谷.BZOJ2179 //将乘数拆成 a0*10^n + a1*10^(n-1) + ... + a_n-1的形式 //可以发现多项式乘法就模拟了竖式乘法 所以用FFT即可 注意处理进位 //n位*n位最多就只有2n位了 //论putchar的速度..还是快的 #include <cmath> #include <cstdio> #include <cctype> #include <algorithm> #define gc() getchar

洛谷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)

bzoj 1977 洛谷P4180 严格次小生成树

Description: 给定一张N个节点M条边的无向图,求该图的严格次小生成树.设最小生成树边权之和为sum,那么严格次小生成树就是边权之和大于sum的最小的一个 Input: 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z. Output: 包含一行,仅一个数,表示严格次小生成树的边权和.(数据保证必定存在严格次小生成树) 思路:先求出原图的最小生成树,然后继续从小到大枚举边(x,y),对于x,y用倍

洛谷.4180.[模板]次小生成树Tree(Kruskal LCA 倍增)

构建完MST后,枚举非树边(u,v,w),在树上u->v的路径中找一条权值最大的边(权为maxn),替换掉它这样在 w=maxn 时显然不能满足严格次小.但是这个w可以替换掉树上严格小于maxn的次大边用倍增维护MST上路径的最大值.次大值,每条非树边的查询复杂度就为O(logn) ps:1.倍增更新次大值时,未必是从最大值转移,要先赋值较大的次大值,再与较小的那个最大值比较.2.maxn!=w时,是可以从maxn更新的(不能更新就是上面情况啊)倍增处理部分我还是在dfs里写吧 md改了一晚上