洛谷.4234.最小差值生成树(LCT)

题目链接

先将边排序,这样就可以按从小到大的顺序维护生成树,枚举到一条未连通的边就连上,已连通则(用当前更大的)替换掉路径上最小的边,这样一定不会更差。
每次构成树时更新答案。答案就是当前边减去生成树上最小边的权值。
LCT上维护最小边的编号。求最小边把树上的边用vis[]标记即可。

不熟啊.

(另外暴力可以排序后枚举一个分界点,在它之后求最小生成树,在它之前求最大生成树)

#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define INF 10000007
const int N=5e4+5,M=2e5+5,S=N+M;

int n,m,_fa[N];
bool vis[M];
struct Edge
{
    int fr,to,val;
    Edge() {}
    Edge(int f,int t,int v): fr(f),to(t),val(v) {}
    bool operator <(const Edge &a)const {
        return val<a.val;
    }
}e[M];
namespace LCT
{
    #define lson son[x][0]
    #define rson son[x][1]

    int fa[S],son[S][2],sk[S],pos[S],val[S];
    bool tag[S];
    inline int Get(int x,int y){//取最小的边
        return val[x]<val[y]?x:y;
    }
    inline void Update(int x){
        pos[x]=Get(x,Get(pos[lson],pos[rson]));//是左右儿子的最小边pos[]!
    }
    inline bool n_root(int x){
        return son[fa[x]][0]==x||son[fa[x]][1]==x;
    }
    inline void Rev(int x){
        std::swap(lson,rson), tag[x]^=1;
    }
    void PushDown(int x){
        if(tag[x]) Rev(lson),Rev(rson),tag[x]=0;
    }
    void Rotate(int x)
    {
        int a=fa[x],b=fa[a],l=son[a][1]==x,r=l^1;
        if(n_root(a)) son[b][son[b][1]==a]=x;
        if(son[x][r]) fa[son[x][r]]=a;
        fa[a]=x, fa[x]=b, son[a][l]=son[x][r], son[x][r]=a;
        Update(a);
    }
    void Splay(int x)
    {
        int t=1,a=x; sk[1]=x;
        while(n_root(a)) sk[++t]=a=fa[a];
        while(t) PushDown(sk[t--]);
        while(n_root(x))
        {
            if(n_root(a=fa[x])) Rotate(son[a][1]==x^son[fa[a]][1]==a?x:a);
            Rotate(x);
        }
        Update(x);
    }
    void Access(int x){
        for(int pre=0; x; x=fa[pre=x])
            Splay(x), rson=pre, Update(x);
    }
    void Make_root(int x){
        Access(x), Splay(x), Rev(x);
    }
    void Split(int x,int y){
        Make_root(x), Access(y), Splay(y);
    }
    void Link(int x){
        Make_root(e[x].to), fa[fa[e[x].to]=x+N]=e[x].fr;
    }
    void Cut(int x){
        Access(e[x-N].to), Splay(x), lson=rson=fa[lson]=fa[rson]/*=fa[x]*/=0;//fa[x]应该是用不到吧。。注意更新顺序!后更新lson,rson...
    }
}
using namespace LCT;

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
int Get_fa(int x){
    return x==_fa[x]?x:_fa[x]=Get_fa(_fa[x]);
}

int main()
{
    n=read(),m=read();
    val[0]=INF;//空节点!
    for(int i=1; i<=n; ++i) _fa[i]=i, val[i]=INF;//点有自己的权值,设为INF避免影响答案(最小边)
    for(int u,v,w,i=1; i<=m; ++i) u=read(),v=read(),w=read(),e[i]=Edge(u,v,w);
    std::sort(e+1,e+1+m);
//  for(int i=1; i<=m; ++i) val[i+N]=e[i].val;
    int res=1e6;
    for(int p=1,x,y,k=1,i=1; i<=m; ++i)
    {
        val[i+N]=e[i].val, x=e[i].fr, y=e[i].to;
        if(k<n && Get_fa(x)!=Get_fa(y))
        {
            _fa[_fa[x]]=_fa[y];//路径压缩后
            Link(i), vis[i]=1;
            if(++k>=n) res=e[i].val-e[p].val;
        }
        else if(x!=y)//不要计算重边
        {
            Split(x,y);
            vis[pos[y]-N]=0, vis[i]=1;//别忘了Splay(y)后再用y的信息pos[y]!而且要先用pos[y]清vis再Cut().
            while(!vis[p]) ++p;
            Cut(pos[y]), Link(i);
            if(k>=n) res=std::min(res,e[i].val-e[p].val);
        }
    }
    printf("%d",res);

    return 0;
}

原文地址:https://www.cnblogs.com/SovietPower/p/8637929.html

时间: 2024-08-30 14:43:00

洛谷.4234.最小差值生成树(LCT)的相关文章

洛谷P4234 最小差值生成树【LCT】

题目描述 给定一个标号为从 1 到 n 的.有 m 条边的无向图,求边权最大值与最小值的差值最小的生成树. 输入格式: 第一行两个数 n, m ,表示图的点和边的数量. 第二行起 m 行,每行形如$ u_i, v_i, w_i$ ,代表 $u_i$ 到 $v_i$有一条长为 $w_i$的无向边. 输出格式: 输出一行一个整数,代表你的答案. 数据保证存在至少一棵生成树. 输入样例#1: 4 6 1 2 10 1 3 100 1 4 90 2 3 20 2 4 80 3 4 40 输出样例#1:

luogu 4234 最小差值生成树 LCT

感觉码力严重下降~ #include <bits/stdc++.h> #define N 400006 #define inf 1000000000 #define setIO(s) freopen(s".in","r",stdin) using namespace std; multiset<int>S; multiset<int>::iterator it; struct Edge { int u,v,c; Edge(int

luogu4234 最小差值生成树

题目大意 在一个带权无向图中,它的最小差值生成树为最大边与最小边差值最小的生成树.求一个图的最小差值生成树. 题解 30分解法 引理1 最小生成树的最大边的边权是所有生成树中最大边边权中的最小值. 证明:任意一棵生成树都可以在最小生成树的基础上,通过不断取一个树外边e,将其替换掉其与生成树所在环中的一条边的方式而得到.我们就看看第一条用来替换的边的情况吧.在不在最小生成树中的边中任取一个边权小于最小生成树最大边m的边e,则e必然与最小生成树的树边形成环.若m不在环中,那么就是替换掉任意一条边,答

洛谷P3348 [ZJOI2016]大森林(LCT,虚点,树上差分)

洛谷题目传送门 思路分析 最简单粗暴的想法,肯定是大力LCT,每个树都来一遍link之类的操作啦(T飞就不说了) 考虑如何优化算法.如果没有1操作,肯定每个树都长一样.有了1操作,就来仔细分析一下对不同树的影响. 假设有一个2操作形如\(l\ r\ x\),那么从微观来看差异,我们只关注第l-1棵树和第l棵树.再假设以后都没有了1操作,那么我们可以认为,第l棵树是第l-1棵树把这个1操作以后嫁接在原来生长节点上的所有节点(以及所有子树)全部转而嫁接到x上.再看第r棵树和第r+1棵树,是不是可以认

luoguP4234 最小差值生成树

https://www.luogu.org/problemnew/show/P4234 按照边的权值从小到大排序,依次加入,并删除能够删除的权值最小的一条边,用 set 维护当前所有边的边权,并查集维护联通性,LCT 维护两点间最小值和 link cut 操作即可 #include <bits/stdc++.h> #define mp make_pair using namespace std; typedef unsigned long long ull; typedef long long

洛谷 P1351 联合权值(NOIp2014D1T2)

题目描述 无向连通图G 有n 个点,n - 1 条边.点从1 到n 依次编号,编号为 i 的点的权值为W i ,每条边的长度均为1 .图上两点( u , v ) 的距离定义为u 点到v 点的最短距离.对于图G 上的点对( u, v) ,若它们的距离为2 ,则它们之间会产生Wu×Wv 的联合权值. 请问图G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? 输入输出格式 输入格式: 输入文件名为link .in. 第一行包含1 个整数n . 接下来n - 1 行,每

洛谷——P1351 联合权值

https://www.luogu.org/problem/show?pid=1351 题目描述 无向连通图G 有n 个点,n - 1 条边.点从1 到n 依次编号,编号为 i 的点的权值为W i ,每条边的长度均为1 .图上两点( u , v ) 的距离定义为u 点到v 点的最短距离.对于图G 上的点对( u, v) ,若它们的距离为2 ,则它们之间会产生Wu×Wv 的联合权值. 请问图G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? 输入输出格式 输入格式

洛谷1351 联合权值

题目描述 无向连通图G 有n 个点,n - 1 条边.点从1 到n 依次编号,编号为 i 的点的权值为W i   ,每条边的长度均为1 .图上两点( u ,  v ) 的距离定义为u 点到v 点的最短距离.对于图G 上的点对( u, v) ,若它们的距离为2 ,则它们之间会产生Wu×Wv 的联合权值. 请问图G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? 输入输出格式 输入格式: 输入文件名为link .in. 第一行包含1 个整数n . 接下来n - 1

[NOIP2014] 提高组 洛谷P1351 联合权值

题目描述 无向连通图G 有n 个点,n - 1 条边.点从1 到n 依次编号,编号为 i 的点的权值为W i ,每条边的长度均为1 .图上两点( u , v ) 的距离定义为u 点到v 点的最短距离.对于图G 上的点对( u, v) ,若它们的距离为2 ,则它们之间会产生Wu ×Wv 的联合权值. 请问图G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? 输入输出格式 输入格式: 输入文件名为link .in. 第一行包含1 个整数n . 接下来n - 1 行,