1. 程式人生 > >BZOJ 2594: [Wc2006]水管局長資料加強版(lct)

BZOJ 2594: [Wc2006]水管局長資料加強版(lct)

傳送門

解題思路

  一道\(lct\)維護動態最小生成樹。剛開始寫了一遍瘋狂\(Re\),冷靜了一下重新寫了一遍終於過了。首先題目中要求兩點之間最大值的最小值,其實就是維護一個最小生成樹,每次詢問最大值。要將刪邊轉化成加邊操作,就是倒著處理,這裡用\(set\)\(map\)就比較方便。然後還要把邊權轉化成點權,具體來說就是假如\(x,y\)直接有一條權值為\(z\)的邊,那麼就新開一個節點,令這個點的權值為\(z\),然後連線\(x\)與這個節點,\(y\)與這個節點。\(lct\)裡維護一下最大值的節點的編號。每當假如一條邊時要判斷一下這條路徑的最大值是否大於新加入的邊,大於的話就先\(cut\)

掉最大值,然後再\(link\)

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<map>
#include<set>

using namespace std;
const int MAXN = 100005;
const int MAXM = 1000005;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return f?x:-x;
} 

int n,m,q,num,fa[MAXN+MAXM],ch[MAXN+MAXM][2],Max[MAXN+MAXM];
int F[MAXN],val[MAXN+MAXM],tot,xx[MAXN+MAXM],yy[MAXN+MAXM];
int cnt,ans[MAXN];
bool tag[MAXN+MAXM];
map<pair<int,int>,int > mp;
set<pair<int,int> > S;

struct Edge{
    int x,y,z,id;
    friend bool operator<(const Edge A,const Edge B){
        return A.z<B.z;
    }
}edge[MAXM];

struct Query{
    int x,y,type,id;
}ask[MAXN];

int Find(int x){
    if(x==F[x]) return x;
    return F[x]=Find(F[x]);
}

inline bool check(int x){
    return (x==ch[fa[x]][1]);
}

inline bool isroot(int x){
    return (x!=ch[fa[x]][0] && x!=ch[fa[x]][1]);
}

inline void pushup(int x){
    Max[x]=x;
    if(val[Max[ch[x][0]]]>val[Max[x]]) Max[x]=Max[ch[x][0]];
    if(val[Max[ch[x][1]]]>val[Max[x]]) Max[x]=Max[ch[x][1]];
}

inline void pushdown(int x){
    if(tag[x]){
        tag[x]=0;swap(ch[x][0],ch[x][1]);
        if(ch[x][0]) tag[ch[x][0]]^=1;
        if(ch[x][1]) tag[ch[x][1]]^=1;
    }
}

void pd(int x){
    if(!isroot(x)) pd(fa[x]);pushdown(x);
}

inline void rotate(int x){
    int y=fa[x],z=fa[y];bool chk=check(x);
    if(!isroot(y)) ch[z][check(y)]=x;
    ch[y][chk]=ch[x][chk^1];fa[ch[x][chk^1]]=y;
    ch[x][chk^1]=y;fa[y]=x;fa[x]=z;pushup(y);pushup(x);
}

inline void splay(int x){
    pd(x);
    for(;!isroot(x);rotate(x))
        if(!isroot(fa[x])) rotate(check(fa[x])==check(x)?fa[x]:x);
}

inline void access(int x){
    for(int y=0;x;y=x,x=fa[x]){
        splay(x);ch[x][1]=y;pushup(x);
    }
}

inline void makeroot(int x){
    access(x);splay(x);tag[x]^=1;
}

inline void link(int x,int y){
    makeroot(x);fa[x]=y;pushup(y);
}

inline void cut(int x,int y){
    makeroot(x);access(y);splay(y);
    ch[y][0]=fa[x]=0;pushup(y);
}

inline void split(int x,int y){
    makeroot(x);access(y);splay(x);
}

int main(){
    n=rd(),m=rd(),q=rd();int x,y,z;num=n;
    for(int i=1;i<=n;i++) F[i]=i;
    for(int i=1;i<=m;i++) {
        x=rd(),y=rd(),z=rd();if(x>y) swap(x,y);
        edge[i].x=x,edge[i].y=y,edge[i].z=z;
        mp[make_pair(x,y)]=z;
    }
    for(int i=1;i<=q;i++){
        z=rd(),x=rd(),y=rd();if(x>y) swap(x,y);
        if(z==2) S.insert(make_pair(x,y));
        ask[i].type=z;ask[i].x=x;ask[i].y=y;
    }
    sort(edge+1,edge+1+m);int u,v;
    for(int i=1;i<=m;i++){
        x=edge[i].x,y=edge[i].y;
        if(S.find(make_pair(x,y))!=S.end()) continue;
        u=Find(x);v=Find(y);if(u==v) continue;
        F[u]=v;val[++num]=edge[i].z;link(x,num);link(num,y);
        xx[num]=x;yy[num]=y;tot++;if(tot==n-1) break;
    }int x1,x2;
    for(int i=q;i;i--){
        x=ask[i].x;y=ask[i].y;
        if(ask[i].type==1) {split(x,y);ans[++cnt]=val[Max[x]];}
        else{
            val[++num]=mp[make_pair(x,y)];
            split(x,y);if(val[Max[x]]<=val[num]) continue;
            x1=xx[Max[x]];x2=Max[x]; //!!!注意這裡要把值拿出來,否則第一個$cut$的時候有可能改變這裡的值。
            cut(xx[Max[x]],Max[x]);cut(x2,x1);
            link(x,num);link(num,y);xx[num]=x;yy[num]=y;
        }
    }
    for(int i=cnt;i;i--) printf("%d\n",ans[i]);
    return 0;
}