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

20

说明

对于 30% 的数据,满足1≤n≤100,1≤m≤1000

对于 97% 的数据,满足 1≤n≤500,1≤m≤100000

对于 100% 的数据,满足 100001≤n≤50000,1≤m≤200000,1≤wi≤10000

***************************

题目分析:

对于要维护边权的lct

通常的做法是给每条边编号为n+1到n+m

对于link(u,v)操作

假设链接u和v的边编号为num

那么我们分别link(u,num),link(v,num)

相对的cut(u,v)

我们分别cut(u,num),cut(v,num)

知道再怎样维护边权后

对于这道题

先将边按边权升序排序

依次遍历每条边

若当前边 i 的两端结点u,v不连通,就link(u,i+n),link(v,i+n)

若当前边 i 的两端结点u,v已连通

找到树上u到v路径上边权最小的边num

先断开num这条边,再加入 i 这条边

cut(u,num),cut(v,num)

link(u,i+n),link(v,i+n)

每便利一条边

若当前树上边数==n-1,就更新答案

对于更新答案时树上最小边权

需要一个vis数组判断第i号边是否在树上

并在每次断边后更新最小边权

具体见代码


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}
    while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}
    return f*x;
}

int n,m;
struct node{int u,v,dis;}E[200010];
int ch[500010][2],fa[500010],lzy[500010];
int val[500010],minn[500010];
int st[500010];
int vis[500010];//i号边是否在树上
int mx,mi=1,ans=1e9;//mx树上当前最大边权编号,mi树上当前最小边权编号

bool cmp(node a,node b){return a.dis<b.dis;}

void update(int x)
{
    minn[x]=x;
    minn[x]=val[minn[ch[x][0]]]<val[minn[x]] ?minn[ch[x][0]]:minn[x];
    minn[x]=val[minn[ch[x][1]]]<val[minn[x]] ?minn[ch[x][1]]:minn[x];
}

void push(int x)
{
    if(!lzy[x]) return;
    swap(ch[x][0],ch[x][1]);
    lzy[ch[x][0]]^=1; lzy[ch[x][1]]^=1;
    lzy[x]=0;
}

int isrt(int x)
{
    return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}

void rotate(int x)
{
    int y=fa[x],z=fa[y];
    int d=(ch[y][0]==x);
    if(!isrt(y))
    {
        if(ch[z][0]==y) ch[z][0]=x;
        else ch[z][1]=x;
    }
    fa[y]=x; fa[ch[x][d]]=y; fa[x]=z;
    ch[y][d^1]=ch[x][d]; ch[x][d]=y;
    update(y); update(x);
}

void splay(int x)
{
    int top=0; st[++top]=x;
    for(int i=x;!isrt(i);i=fa[i])
    st[++top]=fa[i];
    while(top) push(st[top--]);

    while(!isrt(x))
    {
        int y=fa[x],z=fa[y];
        if(!isrt(y))
        {
            if((ch[z][0]==y)^(ch[y][0]==x)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}

void access(int x)
{
    int t=0;
    while(x)
    {
        splay(x);
        ch[x][1]=t;
        update(x);
        t=x; x=fa[x];
    }
}

void mkrt(int x)
{
    access(x); splay(x);
    lzy[x]^=1;
}

void match(int x,int y)
{
    mkrt(x); fa[x]=y;
}

void cut(int x,int y)
{
    mkrt(x);
    access(y); splay(y);
    fa[x]=ch[y][0]=0;
    update(y);
}

int find(int x)//其实理论上用并查集应该会快挺多
{
    access(x); splay(x);
    while(ch[x][0]) x=ch[x][0];
    return x;
}

int get(int x,int y)
{
    mkrt(x);
    access(y); splay(y);
    return minn[y];
}

void solve()
{
    int tot=0;
    for(int i=1;i<=m;++i)
    {
        int u=E[i].u,v=E[i].v; mx=i;
        if(find(u)!=find(v))
        {
            tot++;
            match(u,i+n); match(v,i+n);
            vis[i]=1;
        }
        else
        {
            int num=get(u,v); //获得u到v路径上边权最小的边
            cut(E[num-n].u,num); cut(E[num-n].v,num); vis[num-n]=0;//断边
            match(u,i+n); match(v,i+n); vis[i]=1;//加边
            while(!vis[mi]) mi++;//维护当前树上边权最小值
        }
        if(tot==n-1)ans=min(ans,E[mx].dis-E[mi].dis);//更新答案
    }
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=m;++i)
    {
        E[i].u=read();E[i].v=read();E[i].dis=read();
        if(E[i].u==E[i].v)i--,m--;//判断自环
    }

    sort(E+1,E+1+m,cmp);//minn表示以该节点为根的子树的边权最小的边的编号
    for(int i=1;i<=m;++i)val[i+n]=E[i].dis,minn[i+n]=i+n;
    for(int i=0;i<=n;++i)val[i]=1e9,minn[i]=i;//注意0号也要赋最大值

    solve();
    cout<<ans;
    return 0;
}

原文地址:https://www.cnblogs.com/niiick/p/8900672.html

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

洛谷P4234 最小差值生成树【LCT】的相关文章

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

题目链接 先将边排序,这样就可以按从小到大的顺序维护生成树,枚举到一条未连通的边就连上,已连通则(用当前更大的)替换掉路径上最小的边,这样一定不会更差. 每次构成树时更新答案.答案就是当前边减去生成树上最小边的权值. LCT上维护最小边的编号.求最小边把树上的边用vis[]标记即可. 不熟啊. (另外暴力可以排序后枚举一个分界点,在它之后求最小生成树,在它之前求最大生成树) #include <cstdio> #include <cctype> #include <algor

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 行,