LCT——BZOJ3669/Luogu2387 [Noi2014]魔法森林

题面:BZOJ3669 Luogu2387
LCT维护动态最小生成树
具体怎么操作呢,如果当前加边后树中形成了环,那么就把环上边权最大的边删掉,再把这条边加进去(当然啦如果这条边比环里的边都大就不要加)
这就是维护动态最小生成树,这些操作都可以用LCT来解决
LCT维护边权最大值(只不过维护的时候要记录的是编号)
然后最后答案的话在每次操作之后都询问一下从1到n的最大边权
这个也只要直接access就好了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<string>
#include<ctime>
#include<queue>
#include<climits>
using namespace std;
struct ppap{int x,y,a,b;}a[100001];
inline bool cmp(ppap a,ppap b){return a.a<b.a;}
int t[200001][2],fa[200001],la[200001];
int n,m,zh[200001],v[200001],s[200001];
inline void pushup(int x){
    s[x]=x;if(v[s[t[x][0]]]>v[s[x]])s[x]=s[t[x][0]];
    if(v[s[t[x][1]]]>v[s[x]])s[x]=s[t[x][1]];
}
inline void pushdown(int x){
    if(la[x]){
        swap(t[x][1],t[x][0]);
        la[t[x][1]]^=1;la[t[x][0]]^=1;
    }
    la[x]=0;
}
inline bool rt(int x){
    return t[fa[x]][0]!=x&&t[fa[x]][1]!=x;
}
inline void turn(int x){
    int y=fa[x],z=fa[y],l=(x!=t[fa[x]][0]),r=(y!=t[fa[y]][0]);
    int p=t[x][l^1];
    if(!rt(y))t[z][r]=x;
    fa[p]=y;fa[x]=z;t[x][l^1]=y;fa[y]=x;t[y][l]=p;
    pushup(y);pushup(x);
}
inline void splay(int x){
    int i=x,y,z;for(;!rt(i);i=fa[i])zh[++zh[0]]=i;
    zh[++zh[0]]=i;while(zh[0])pushdown(zh[zh[0]--]);
    while(!rt(x)){
        y=fa[x];z=fa[y];
        if(!rt(y)){
            if((t[y][0]==x)^(t[z][0]==y))turn(x);
            else turn(y);
        }
        turn(x);
    }
}
inline void access(int x){
    int p=0;
    while(x){
        splay(x);t[x][1]=p;
        p=x;pushup(x);x=fa[x];
    }
}
inline void chrt(int x){
    access(x);splay(x);la[x]^=1;
}
inline void link(int x,int y){
    chrt(x);fa[x]=y;
}
inline void cut(int x,int y){
    chrt(x);access(y);splay(y);
    t[y][0]=fa[x]=0;pushup(y);
}
inline int askrt(int x){
    access(x);splay(x);int p=x;pushdown(x);
    while(t[p][0])p=t[p][0],pushdown(p);
    return p;
}
int main()
{
    scanf("%d%d",&n,&m);int ans=1e9;
    for(int i=1;i<=m;i++)scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].a,&a[i].b);
    sort(a+1,a+m+1,cmp);
    for(int i=1;i<=m;i++){
        int X=a[i].x,Y=a[i].y,A=a[i].a,B=a[i].b;
        if(askrt(X)==askrt(Y)){
            chrt(X);access(Y);splay(Y);int p=s[Y];
            if(v[p]>B)cut(p,a[p-n].x),cut(p,a[p-n].y);
            else{
                if(askrt(1)==askrt(n)){
                    chrt(1);access(n);splay(n);
                    ans=min(ans,A+v[s[n]]);
                }
                continue;
            }
        }
        v[n+i]=B;s[n+i]=n+i;link(X,n+i);link(Y,n+i);
        if(askrt(1)==askrt(n)){
            chrt(1);access(n);splay(n);
            ans=min(ans,A+v[s[n]]);
        }
    }
    if(ans==1e9)puts("-1");
    else printf("%d",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值