P4234(最小差值生成树 lct维护生成树)

题目
在这里插入图片描述
先把所有边按x从小到大排序。动态添边。x y 已经有一条路径 就把x y路径中最小的边去掉,换成这个边。 每次(num==n-1)时 更新答案 ans=min(ans,e[i].w-e[dex].w);
dex是当前生成树中最小的边。注意是生成树上,不是路径上,lct搞不来的。
while(!vis[dex]) ++dex; 想一想,这肯定是对的吧。每次假如的新边都是大边,所以最小边在加边的过程不可能 变小。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
inline int rd(){
    char ch=getchar();int x=0,op=1;
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') op=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*op;
}
const int N=5e4+5,M=2e5+5,INF=0x3f3f3f3f;
struct node{int x,y,w;}e[M];
inline int cmp(node A,node B){return A.w<B.w;}
int f[N+M],ch[N+M][2],st[N+M],tag[N+M],val[N+M],mi[N+M],F[N];
inline int seek(int x){return (x==F[x])?(x):(F[x]=seek(F[x]));}
inline void combine(int x,int y){F[seek(x)]=seek(y);}
inline void pushup(int x){
    mi[x]=val[x];
    if(e[mi[ch[x][0]]].w<e[mi[x]].w) mi[x]=mi[ch[x][0]];
    if(e[mi[ch[x][1]]].w<e[mi[x]].w) mi[x]=mi[ch[x][1]];
}
inline void pushdown(int x){
    if(!tag[x]) return;
    swap(ch[ch[x][0]][0],ch[ch[x][0]][1]),tag[ch[x][0]]^=1;
    swap(ch[ch[x][1]][0],ch[ch[x][1]][1]),tag[ch[x][1]]^=1;
    tag[x]=0;
}
inline int notroot(int x){
    return ch[f[x]][0]==x||ch[f[x]][1]==x;
}
inline void route(int x){
    int y=f[x],z=f[y],w=(ch[y][1]==x);
    if(notroot(y)) ch[z][ch[z][1]==y]=x;
    f[x]=z;
    ch[y][w]=ch[x][w^1];if(ch[y][w]) f[ch[y][w]]=y;
    ch[x][w^1]=y,f[y]=x;
    pushup(y),pushup(x);
}
inline void splay(int x){
    int y=x,num=0;st[++num]=y;
    while(notroot(y)) st[++num]=(y=f[y]);
    while(num) pushdown(st[num--]);
    if(!notroot(x)) return;
    for(int fa=f[x];notroot(x);route(x),fa=f[x])
        if(notroot(fa)) ((ch[fa][1]==x)^(ch[f[fa]][1]==fa))?route(x):route(fa);
}
inline void access(int x){
    for(int y=0;x;x=f[y=x])
        splay(x),ch[x][1]=y,pushup(x);
}
inline void makeroot(int x){
    access(x),splay(x);
    swap(ch[x][0],ch[x][1]),tag[x]^=1;
}
inline void split(int x,int y){
    makeroot(x),access(y),splay(y);
}
inline void link(int x,int y){
    makeroot(x),f[x]=y;
}
inline void cut(int x,int y){
    split(x,y),f[x]=ch[y][0]=0,pushup(y);
}
int vis[M];
int main(){
    int n=rd(),m=rd();
    for(int i=0;i<=n;++i) F[i]=i;
    for(int i=1;i<=m;++i) mi[i+n]=val[i+n]=i;
    for(int i=1;i<=m;++i) e[i]=(node){rd(),rd(),rd()};
    sort(e+1,e+m+1,cmp);e[0].w=INF;
    int ans;
    for(int i=1,num=0,dex=1,x,y;i<=m;++i){
        x=e[i].x,y=e[i].y;
        if(x==y) continue;
        if(seek(x)!=seek(y)){
            vis[i]=1,++num,link(x,i+n),link(y,i+n),combine(x,y);
            if(num==n-1){
                while(!vis[dex]) ++dex;
                ans=e[i].w-e[dex].w;
            }
        }
        else{
            split(x,y);int id=mi[y];
            cut(e[id].x,id+n),cut(e[id].y,id+n),vis[id]=0;
            link(x,i+n),link(y,i+n),vis[i]=1;
            if(num==n-1){
                while(!vis[dex]) ++dex;
                ans=min(ans,e[i].w-e[dex].w);
            }
        }
    }
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值