「BZOJ3669 UOJ#3 LOJ2245」【NOI2014】魔法森林【Link Cut Tree】

【NOI2014】魔法森林

爲了得到書法大家的真傳,小EE同學下定決心去拜訪住在魔法森林中的隱士。魔法森林可以被看成一個包含個NN節點MM條邊的無向圖,節點標號爲 1n1…n,邊標號爲1m1…m。初始時小E同學在 11 號節點,隱士則住在 nn 號節點。小E需要通過這一片魔法森林,才能夠拜訪到隱士。

魔法森林中居住了一些妖怪。每當有人經過一條邊的時候,這條邊上的妖怪就會對其發起攻擊。幸運的是,在 11 號節點住着兩種守護精靈:AA型守護精靈與BB型守護精靈。小EE可以藉助它們的力量,達到自己的目的。

只要小EE帶上足夠多的守護精靈,妖怪們就不會發起攻擊了。具體來說,無向圖中的每一條邊 eiei 包含兩個權值 aiai 與 bibi。若身上攜帶的A型守護精靈個數不少於 aia_i,且B型守護精靈個數不少於 bib_i,這條邊上的妖怪就不會對通過這條邊的人發起攻擊。當且僅當通過這片魔法森林的過程中沒有任意一條邊的妖怪向小EE發起攻擊,他才能成功找到隱士。

由於攜帶守護精靈是一件非常麻煩的事,小EE想要知道,要能夠成功拜訪到隱士,最少需要攜帶守護精靈的總個數。守護精靈的總個數爲AA型守護精靈的個數與BB型守護精靈的個數之和。

輸入格式

11行包含兩個整數 n,mn,m,表示無向圖共有 nn 個節點,mm 條邊。

接下來 mm 行,第 i+1i+1 行包含44個正整數 xi,yi,ai,bix_i,y_i,a_i,b_i,描述第 ii 條無向邊。其中 xix_iyiy_i 爲該邊兩個端點的標號,aia_ibib_i的含義如題所述。

注意數據中可能包含重邊與自環。

輸出格式

輸出一行一個整數:如果小EE可以成功拜訪到隱士,輸出小E最少需要攜帶的守護精靈的總個數;如果無論如何小EE都無法拜訪到隱士,輸出1“-1”(不含引號)。

樣例一

input
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
output
32

explanation

如果小E走路徑124,需要攜帶 19+15=3419+15=34 個守護精靈;

如果小E走路徑134,需要攜帶 17+17=3417+17=34 個守護精靈;

如果小E走路徑1234,需要攜帶 19+17=3619+17=36 個守護精靈;

如果小E走路徑1324,需要攜帶 17+15=3217+15=32 個守護精靈。

綜上所述,小E最少需要攜帶 3232 個守護精靈。

樣例二

input
3 1
1 2 1 1
output
-1

explanation

EE無法從11號節點到達33號節點,故輸出1-1

題意

  • 就是給你一張無向圖,每一條邊都有兩個屬性aia_ibib_i,你擁有兩個屬性值AABB,能通過一條邊當且僅當A>aiA>a_iB>biB>b_i,通過後AABB不變,求滿足能從11走到nn的最小A+BA+B

題解

  • 將所有邊按照屬性aa排序,然後遍歷所有邊,用LCTLCT維護屬性bb的最小生成樹,當遇到環時,即即將加入的邊的兩個端點已經在同一個聯通塊中時,找到現在樹中從uiu_iviv_i的最大值位置pospos,如果bi<bposb_i<b_{pos},則先將樹從pospos處斷開,然後再加入當前邊,否則判斷1與n是否在同一個集合中,如果是,查詢最大值並對ansansminmin
  • 這裏小技巧是將邊權轉化爲點權的方法:兩點之間再新建一個節點,節點權值設爲邊權

代碼

#include<bits/stdc++.h>

using namespace std;
const int maxn=2e5+10;
#define inf 0x3f3f3f3f

namespace LCT{
    int ch[maxn][2],fa[maxn],sta[maxn],mark[maxn];
    int val[maxn],ans[maxn];
    inline bool not_root(int x) {
        return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
    }
    inline int dir(int x) {
        return ch[fa[x]][1]==x;
    }
    inline void add_mark(int x) {  //將x這顆子樹翻轉
        swap(ch[x][0],ch[x][1]);
        mark[x]^=1;
    }

    inline void push_down(int x) {
        if(mark[x]) {
            if(ch[x][0]) add_mark(ch[x][0]);
            if(ch[x][1]) add_mark(ch[x][1]); 
            mark[x]=0;
        }
    }

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

    inline void pushall(int x) {
        if(not_root(x)) pushall(fa[x]);
        push_down(x);
    }

    inline void rotate(int x){
        int y=fa[x],z=fa[y],k=dir(x);
        if(ch[x][k^1]) fa[ch[x][k^1]]=y;ch[y][k]=ch[x][k^1];
        if(not_root(y)) ch[z][dir(y)]=x;fa[x]=z;
        ch[x][k^1]=y;fa[y]=x;
        push_up(y);push_up(x);
    }
    inline void splay(int x,int goal=0) {
        pushall(x);
        while(not_root(x)) {
            int y=fa[x],z=fa[y];
            if(not_root(y)) {
                if(dir(x)==dir(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        //push_up(x);
    }

    inline void access(int x) {    //從原樹的根向x拉一條實鏈
        for(int y=0;x;y=x,x=fa[x]) {
            splay(x);ch[x][1]=y;push_up(x);
        }
    }

    inline void make_root(int x) {  //使x成爲splay的根
        access(x);splay(x);add_mark(x);
    }

    inline int find_root(int x) {   //找到x在原樹中的根
        access(x);splay(x);
        while(ch[x][0]) push_down(x),x=ch[x][0];
        splay(x);
        return x;
    }

    inline void split(int x,int y) {   //拉出一條x->y的實鏈,y爲splay根
        make_root(x);access(y);splay(y);
    }

    inline bool link(int x,int y) {    //連接x與y,若已經在同一顆原樹中,返回0
        make_root(x);
        if(find_root(y)==x) return 0;
        fa[x]=y;return 1;
    } 
    inline bool cut(int x,int y) {
        make_root(x);
        if(find_root(y)!=x||fa[y]!=x||ch[y][0]) return 0;
        fa[y]=ch[x][1]=0;
        push_up(x);
        return 1;
    }
    inline int query(int l,int r) {
        split(l,r);
        return ans[r];
    }
};
using namespace LCT;

struct edge{
    int u,v,a,b;
    friend bool operator<(const edge &c,const edge &d) {
        return c.a<d.a;
    }
}o[maxn];

int n,m;
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d %d %d %d",&o[i].u,&o[i].v,&o[i].a,&o[i].b);
    sort(o+1,o+m+1);
    int ans=inf;
    for(int i=1;i<=m;i++) {
        if(find_root(o[i].u)==find_root(o[i].v)){
            int u=query(o[i].u,o[i].v);
            if(o[i].b<val[u]) {
                cut(o[u-n].u,u);
                cut(o[u-n].v,u);
            }else {
                if(find_root(1)==find_root(n)) ans=min(ans,o[i].a+val[query(1,n)]);
                continue;
            }
        }
        val[i+n]=o[i].b;
        link(o[i].u,i+n);link(o[i].v,i+n);
        if(find_root(1)==find_root(n)) ans=min(ans,o[i].a+val[query(1,n)]);
    } 
    if(ans==inf) printf("%d\n",-1);
    else printf("%d\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章