[BeiJing2010组队]次小生成树 Tree

Description
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

Input
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

Output
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6

Sample Output
11

HINT
数据中无向图无自环;
50% 的数据N≤2 000 M≤3 000;
80% 的数据N≤50 000 M≤100 000;
100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

首先建出最小生成树,然后对于非树边(u,v),它能产生贡献,当且仅当u,v在树上路径上有条边被切断了。于是我们用树剖找出路径上最长的边,将其用(u,v)替换,即可得到次小生成树了。但我们需要得到严格次小生成树,那么就会出现路径上最长的边和w(u,v)相同的情况,这是我们就需要找到一条次小边,那么线段树维护两个值即可。

记得判掉重边的影响。。。

/*program from Wolfycz*/
#pragma GCC optimize(3)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)  print(x/10);
    putchar(x%10+'0');
}
const int N=1e5,M=3e5;
int v[N+10],dfn[N+10],ID[N+10];
int n,m;
struct S1{
    #define ls (p<<1)
    #define rs (p<<1|1)
    struct node{
        int f,g;
        node(){f=g=-inf;}
        void insert(int _f,int _g){f=_f,g=_g;}
    }tree[(N<<2)+10];
    friend node operator +(const node &x,const node &y){
        node z;
        z.f=max(x.f,y.f);
        z.g=max(x.g,y.g);
        if (x.f!=y.f)   z.g=max(z.g,min(x.f,y.f));
        return z;
    }
    void build(int p,int l,int r){
        if (l==r){
            tree[p].insert(v[dfn[l]],-inf);
            return;
        }
        int mid=(l+r)>>1;
        build(ls,l,mid),build(rs,mid+1,r);
        tree[p]=tree[ls]+tree[rs];
    }
    node Query(int p,int l,int r,int x,int y){
        if (x<=l&&r<=y) return tree[p];
        int mid=(l+r)>>1; node res;
        if (x<=mid) res=res+Query(ls,l,mid,x,y);
        if (y>mid)  res=res+Query(rs,mid+1,r,x,y);
        return res;
    }
}ST;//Segment Tree
struct S2{
    int pre[(M<<1)+10],now[N+10],child[(M<<1)+10],val[(M<<1)+10];
    int top[N+10],Rem[N+10],deep[N+10],size[N+10],fa[N+10];
    int tot,Time;
    void join(int x,int y,int z){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z;}
    void insert(int x,int y,int z){join(x,y,z),join(y,x,z);}
    void build(int x){
        deep[x]=deep[fa[x]]+1,size[x]=1;
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa[x]) continue;
            fa[son]=x,v[son]=val[p];
            build(son),size[x]+=size[son];
            if (size[Rem[x]]<size[son]) Rem[x]=son;
        }
    }
    void dfs(int x){
        if (!x) return;
        dfn[ID[x]=++Time]=x;
        top[x]=Rem[fa[x]]==x?top[fa[x]]:x;
        dfs(Rem[x]);
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa[x]||son==Rem[x])    continue;
            dfs(son);
        }
    }
    int Get(int x,int y,int z){
        S1::node res;
        while (top[x]!=top[y]){
            if (deep[top[x]]<deep[top[y]])  swap(x,y);
            res=res+ST.Query(1,1,n,ID[top[x]],ID[x]);//+被重载了,在ST结构体里面
            x=fa[top[x]];
        }
        if (x!=y){
            if (deep[x]>deep[y])    swap(x,y);
            res=res+ST.Query(1,1,n,ID[Rem[x]],ID[y]);
        }
        if (res.f==-inf&&res.g==-inf)   return inf;//开始没判,后面因为重边导致res两个信息都为-inf,于是炸int了。。。
        return z-(z==res.f?res.g:res.f);
    }
}HLD;//Heavy-Light Decomposition
struct S3{
    int fa[N+10];
    void init(){for (int i=1;i<=n;i++)  fa[i]=i;}
    int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
}DSU;//Disjoint Set Union
struct S4{
    int x,y,z,type;
    S4(){type=0;}
    void insert(){x=read(),y=read(),z=read();}
    bool operator <(const S4 &a)const{return z<a.z;}
}A[M+10];
int main(){
    n=read(),m=read();
    for (int i=1;i<=m;i++)  A[i].insert();
    sort(A+1,A+1+m);
    DSU.init();
    ll MST=0;int Ans=inf;
    for (int i=1;i<=m;i++){
        int x=DSU.find(A[i].x),y=DSU.find(A[i].y);
        if (x!=y)   DSU.fa[x]=y,HLD.insert(A[i].x,A[i].y,A[i].z),A[i].type=1,MST+=A[i].z;
    }
    HLD.build(1),HLD.dfs(1),ST.build(1,1,n);
    for (int i=1;i<=m;i++){
        if (A[i].type)  continue;
        Ans=min(Ans,HLD.Get(A[i].x,A[i].y,A[i].z));
    }
    printf("%lld\n",MST+Ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Wolfycz/p/9746698.html

时间: 2024-08-04 08:06:10

[BeiJing2010组队]次小生成树 Tree的相关文章

【BZOJ 1977】 [BeiJing2010组队]次小生成树 Tree

1977: [BeiJing2010组队]次小生成树 Tree Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 2313  Solved: 544 [Submit][Status][Discuss] Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格

1977: [BeiJing2010组队]次小生成树 Tree

题解:和cf的一道题比较类似 首先跑一个MST 对于这个树做树链剖分 枚举不在这个树上的边找严格小于这条边的最大边权值 然后求ans #include <bits/stdc++.h> #define ll long long const int MAXN=1e5+10; const int maxn=3e5+10; const int inf=1e9+20; using namespace std; ll read(){ ll x=0,f=1;char ch=getchar(); while(

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

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

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

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

BZOJ 1977 [BeiJing2010组队]次小生成树 Tree

严格次小生成树.一开始没有特批一圈都相等的情况,一直WA,十分难受. 先生成最小生成树,枚举每条非树边,连上它构成一个环,拆掉环上树边中最大的一条(若和该边相等则次大的一条)换上这条. 用倍增维护一条链上的最大边和次大边,倍增跑lca同时找出环上最大边和次大边,看能否更新答案. #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath

[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) 这下小

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

bzoj1977 [BeiJing2010组队]次小生成树 Tree——严格次小生成树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1977 因为严格,所以要记录到 LCA 的一个次小值: 很快写好,然后改掉一堆错误后终于过了样例!然而交上去1WA: 又改了半天,还是WA,于是放弃,抄题解好久... 然而就在我调了一个小时终于锁定错误就在那个子函数里的时候才突然看到了自己的明显惊天大错误是怎么回事??!!!稍微改了一下下就完美AC... 不过还有点收获,把求各种层次的 f 放在 dfs 函数里会比单独拿出来再求一遍快 10

严格次小生成树(Bzoj1977:[Beijing2010组队]次小生成树)

非严格次小生成树 很简单,先做最小生成树 然后枚举没加入的边加入,替换掉这个环内最大的边 最后取\(min\) 严格次小生成树 还是一样的 可以考虑维护一个严格次大值 最大值和枚举的边相同就替换次大值的边 否则替换最大值的边 最后取\(min\) 裸题 Luogu 随你用各种姿势\(AC\) \(LCT\)常数大,但是好写,开\(O2\)可以过 # include <bits/stdc++.h> # define RG register # define IL inline # define