洛谷P4234 最小差值生成树(lct动态维护最小生成树)

题目描述

给定一个标号为从 11 到 nn 的、有 mm 条边的无向图,求边权最大值与最小值的差值最小的生成树。

输入输出格式

输入格式:

 

第一行两个数 n, mn,m ,表示图的点和边的数量。

第二行起 mm 行,每行形如 u_i, v_i, w_iui,vi,wi ,代表 u_iui 到 v_ivi 间有一条长为 w_iwi 的无向边。

 

输出格式:

 

输出一行一个整数,代表你的答案。

数据保证存在至少一棵生成树。

 

输入输出样例

输入样例#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 \leq n \leq 100, 1 \leq m \leq 10001n100,1m1000

对于 97% 的数据,满足 1 \leq n \leq 500, 1 \leq m \leq 1000001n500,1m100000

对于 100% 的数据,满足 1 \leq n \leq 50000, 1 \leq m \leq 200000, 1 \leq w_i \leq 100001n50000,1m200000,1wi10000

题解:有自环是真的毒瘤……反正思路是从大到小枚举每条边作为最小边构成的生成树,然后显然他要加进去就要把原环上最大值扔掉,这样子动态加边删边,就可以搞出答案了

还有个问题是怎么维护最小生成树里的最大值,因为权值很小,所以可以考虑搞一个cnt,一开始pos等于最大边w,接着如果最大边被删掉了,就暴力往下跳pos,直到cnt[pos]>0为止,这样子均摊一下复杂度是O(1)的

代码如下:

#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 300010
#define M 350010
#define lson ch[x][0]
#define rson ch[x][1]
using namespace std;

int cnt[10010],max1,n,m,ans=0x3f3f3f3f;

struct node
{
    int from,to,w;
} e[N];

int cmp(node a,node b)
{
    return a.w<b.w;
}

//lct begin

int tag[M],f[M],ch[M][2],sum[M];

int not_root(int now)
{
    int x=f[now];
    return lson==now||rson==now;
}

int push_up(int x)
{
    sum[x]=x;
    if(e[sum[lson]].w>e[sum[x]].w)
    {
        sum[x]=sum[lson];
    }
    if(e[sum[rson]].w>e[sum[x]].w)
    {
        sum[x]=sum[rson];
    }
}

int rev(int x)
{
    swap(lson,rson);
    tag[x]^=1;
}

int push_down(int x)
{
    if(tag[x])
    {
        rev(lson);
        rev(rson);
        tag[x]^=1;
    }
}

int rotate(int x)
{
    int y=f[x],z=f[y],kd=ch[y][1]==x,xs=ch[x][!kd];
    if(not_root(y))
    {
        ch[z][ch[z][1]==y]=x;
    }
    ch[x][!kd]=y;
    ch[y][kd]=xs;
    if(xs) f[xs]=y;
    f[y]=x;
    f[x]=z;
    push_up(y);
}

int push_all(int x)
{
    if(not_root(x))
    {
        push_all(f[x]);
    }
    push_down(x);
}

int splay(int x)
{
    int y,z;
    push_all(x);
    while(not_root(x))
    {
        y=f[x],z=f[y];
        if(not_root(y))
        {
            (ch[y][1]==x)^(ch[z][1]==y)?rotate(x):rotate(y);
        }
        rotate(x);
    }
    push_up(x);
}

int access(int x)
{
    for(int y=0; x; y=x,x=f[x])
    {
        splay(x);
        rson=y;
        push_up(x);
    }
}

int make_root(int x)
{
    access(x);
    splay(x);
    rev(x);
}

int split(int x,int y)
{
    make_root(x);
    access(y);
    splay(y);
}

int find_root(int x)
{
    access(x);
    splay(x);
    while(lson)
    {
        push_down(x);
        x=lson;
    }
    return x;
}

int link(int x,int y)
{
    make_root(x);
    if(find_root(y)==x) return 0;
    f[x]=y;
    return 1;
}

int cut(int x,int y)
{
    make_root(x);
    if(find_root(y)!=x||f[x]!=y||rson) return 0;
    f[x]=ch[y][0]=0;
    push_up(y);
    return 1;
}

int print(int x)
{
    if(lson) print(lson);
    printf("%d ",x);
    if(rson) print(rson);
}

//lct end

//dsu begin

int fa[M];

int init()
{
    for(int i=1; i<M; i++)
    {
        fa[i]=i;
    }
}

int find(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}

int unity(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(fx==fy) return 0;
    fa[fx]=fy;
    return 1;
}

//dsu end

int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].w);
    }
    sort(e+1,e+m+1,cmp);
    int tot=0;
    for(int i=m; i>=1; i--)
    {
        if(e[i].from==e[i].to) continue;
        if(unity(e[i].from,e[i].to))
        {
            tot++;
            link(e[i].from+m,i);
            link(e[i].to+m,i);
            cnt[e[i].w]++;
            max1=max(max1,e[i].w);
            if(tot==n-1) ans=max1-e[i].w;
        }
        else
        {
            split(e[i].from+m,e[i].to+m);
            int gg=sum[e[i].to+m];
            cut(e[gg].to+m,gg);
            cut(e[gg].from+m,gg);
            cnt[e[gg].w]--;
            cnt[e[i].w]++;
            while(!cnt[max1])
            {
                max1--;
            }
            if(tot==n-1) ans=min(ans,max1-e[i].w);
            link(e[i].to+m,i);
            link(e[i].from+m,i);
        }
    }
    printf("%d\n",ans);
}

猜你喜欢

转载自www.cnblogs.com/stxy-ferryman/p/9485774.html