码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-04-21 13:31:18      阅读:210      评论:0      收藏:0      [点我收藏+]

标签:ret   out   输出   ||   ==   数据   分析   pac   获得   

题目描述

给定一个标号为从 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;
}

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

标签:ret   out   输出   ||   ==   数据   分析   pac   获得   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!