首页 > 其他 > 详细

P4234 最小差值生成树

时间:2019-05-08 00:24:39      阅读:217      评论:0      收藏:0      [点我收藏+]

lct动态维护生成树

先对边排序

加边,如果两点以联通就断最小的边再加边(可证明最优(之后的边再加入就是最大边,此时最小边变大了,ans更优了))

实现:split(x,y)可以在树上维护id,此时可以通过y来访问x-y路径上的答案

技巧:拆边为点

id为边在lct中的编号(n+1~n+m)

通过判断>n即可确定边与点

连边:

link(edge[i].u,n+i),link(n+i,edge[i].v);

断边:将边splay上来再儿子单方面不认父亲

splay(now);
t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;

 

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+7;
const int M=2e5+7;
int st[N+M],n,m,used[M],num,ans=1<<30,tot;
int read() {
    char cc = getchar(); int cn = 0, flus = 1;
    while(cc < 0 || cc > 9) {  if( cc == - ) flus = -flus;  cc = getchar();  }
    while(cc >= 0 && cc <= 9)  cn = cn * 10 + cc - 0, cc = getchar();
    return cn * flus;
}
struct edge{
    int u,v,val;
    bool operator < (const edge &x)const {
        return val<x.val;
    }
}edge[M];
struct tree{
    int ch[2],fa,id=0;
    bool rev;
}t[N+M];
bool nroot(int x){
    return (t[t[x].fa].ch[0]==x)||(t[t[x].fa].ch[1]==x);
}
void rev(int x){
    swap(t[x].ch[0],t[x].ch[1]);
    t[x].rev^=1;
}
void pushdown(int x){
    if(t[x].rev){
        if(t[x].ch[0])rev(t[x].ch[0]);
        if(t[x].ch[1])rev(t[x].ch[1]);
        t[x].rev=0;
    }
}
void pushup(int x){
    t[x].id=x;//这里要注意写法
    if(t[t[x].ch[0]].id>n&&(t[x].id<=n||t[x].id>t[t[x].ch[0]].id))t[x].id=t[t[x].ch[0]].id;
    if(t[t[x].ch[1]].id>n&&(t[x].id<=n||t[x].id>t[t[x].ch[1]].id))t[x].id=t[t[x].ch[1]].id;
}
void rotate(int x){
    int y=t[x].fa;
    int z=t[y].fa;
    bool k=t[y].ch[1]==x;
    if(nroot(y))t[z].ch[t[z].ch[1]==y]=x;
    t[x].fa=z;
    t[y].ch[k]=t[x].ch[k^1];
    t[t[x].ch[k^1]].fa=y;
    t[x].ch[k^1]=y;
    t[y].fa=x;
    pushup(y);
    pushup(x);
}
void splay(int x){
    int y=0,z=x;
    st[++y]=z;
    while(nroot(z)){
        st[++y]=z=t[z].fa;
    }
    while(y)pushdown(st[y--]);
    while(nroot(x)){
        y=t[x].fa;
        z=t[y].fa;
        if(nroot(y))
            (t[y].ch[1]==x)^(t[z].ch[1]==y)?rotate(x):rotate(y);
        rotate(x);
    }
}
void access(int x){
    for(int y=0;x;y=x,x=t[x].fa){
        splay(x);
        t[x].ch[1]=y;
        pushup(x);
    }
}
void makeroot(int x){
    access(x);
    splay(x);
    rev(x);
}
int findroot(int x){
    access(x);
    splay(x);
    while(t[x].ch[0]){
        pushdown(x);
        x=t[x].ch[0];
    }
    splay(x);
    return x;
}
void split(int x,int y){
    makeroot(x);
    access(y);
    splay(y);
}
void link(int x,int y){
    makeroot(x);
    t[x].fa=y;
}
bool check(int x,int y){
    makeroot(x);
    return findroot(y)==x;
}
int main(){
    n=read();
    m=read();
    for(int i=1;i<=m;i++){
        edge[i].u=read();
        edge[i].v=read();
        edge[i].val=read();
    }
    sort(edge+1,edge+1+m);
    int j=1;
    for(int i=1;i<=m;i++){
        t[n+i].id=n+i;//初始化 
        if(edge[i].u==edge[i].v){
            used[i]=1;//可能有自己连自己的边 
            continue;
        }
        else if(!check(edge[i].u,edge[i].v)){
            link(edge[i].u,n+i),link(n+i,edge[i].v);
            ++num;//连边的次数,为n-1时即可开始更新答案 
        }
        else {
            int now;
            split(edge[i].u,edge[i].v),now=t[edge[i].v].id;
            used[now-n]=1,splay(now);
            t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;
            link(edge[i].u,n+i),link(n+i,edge[i].v);
        }
        while(used[j]&&j<i)++j;//这里这样写相当于维护了指针,每次都再扫一次的话复杂度就炸了
        if(num>=n-1)ans=min(ans,edge[i].val-edge[j].val);
    }
    printf("%d\n",ans);
    return 0;
}

 

非lct做法

话说lct的复杂度怎么算啊

P4234 最小差值生成树

原文:https://www.cnblogs.com/Hikigaya/p/10828979.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!