1. 程式人生 > >洛谷P2387 [NOI2014]魔法森林

洛谷P2387 [NOI2014]魔法森林

無法 date stream 找到 輸入輸出格式 我們 如何 文件的 adc

洛谷P2387 [NOI2014]魔法森林

題目描述

為了得到書法大家的真傳,小 E 同學下定決心去拜訪住在魔法森林中的隱 士。魔法森林可以被看成一個包含 \(n\) 個節點 \(m\) 條邊的無向圖,節點標號為 \(1,2,3,…,n\),邊標號為 \(1,2,3,…,m\)。初始時小 E 同學在 \(1\) 號節點,隱士則住在 \(n\) 號節點。小 E 需要通過這一片魔法森林,才能夠拜訪到隱士。
魔法森林中居住了一些妖怪。每當有人經過一條邊的時候,這條邊上的妖怪 就會對其發起攻擊。幸運的是,在 \(1\) 號節點住著兩種守護精靈:A 型守護精靈與 B 型守護精靈。小 E 可以借助它們的力量,達到自己的目的。
只要小 E 帶上足夠多的守護精靈,妖怪們就不會發起攻擊了。具體來說,無 向圖中的每一條邊 \(e_i\)

包含兩個權值 \(a_i\)\(b_i\) 。若身上攜帶的 A 型守護精靈個數不少於 \(a_i\) ,且 B 型守護精靈個數不少於 \(b_i\) ,這條邊上的妖怪就不會對通過這條邊 的人發起攻擊。當且僅當通過這片魔法森林的過程中沒有任意一條邊的妖怪向 小 E 發起攻擊,他才能成功找到隱士。
由於攜帶守護精靈是一件非常麻煩的事,小 E 想要知道,要能夠成功拜訪到隱士,最少需要攜帶守護精靈的總個數。守護精靈的總個數為 A 型守護精靈的個數與 B 型守護精靈的個數之和。

輸入輸出格式

輸入格式:

輸入文件的第 \(1\) 行包含兩個整數 \(n,m\),表示無向圖共有 \(n\) 個節點,\(m\)

條邊。 接下來 \(m\) 行,第\(i+ 1\) 行包含 \(4\) 個正整數 \(X_i,Y_i,a_i,b_i\),描述第 \(i\) 條無向邊。 其中 \(X_i\)\(Y_i\)為該邊兩個端點的標號,\(a_i\)\(b_i\) 的含義如題所述。 註意數據中可能包含重邊與自環。

輸出格式:

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

思路

動態樹求解動態最小生成樹
從小到大枚舉A型守護精靈的個數,很顯然A的值只會等於\(a_i\)中的一個。
當A的值確定之後,原圖中的\(a_i\)

大於A的值的邊都會被刪去,隨著A的值的不斷增大,會不斷有邊加入。
使用動態樹維護生成樹兩個節點之間\(b_i\)的最大值,那麽每當一條新的邊加入生成樹時,我們可以求出這條新邊兩個端點之前原來的\(b_i\)的最大值,如果這個最大值比\(b_i\)大,那麽找到這條\(b_i\)最大的邊並把它從生成樹中刪除,動態樹中斷開,並把新邊加入,操作時算一下最小值最後輸出就行了。

CODE

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 200010
struct Node{
    int u,v,a,b;
    Node(){}
    Node(int u,int v,int a,int b):u(u),v(v),a(a),b(b){}
    bool operator < (const Node &aa) const{
        return a<aa.a;
    }
}p[MAXN];
int ch[MAXN][2],fa[MAXN],maxid[MAXN],q[MAXN],linkline[MAXN][2],val[MAXN];
bool rev[MAXN];
int i,j,k,m,n,x,y,u,v,a,b,qtop,ans,ncnt;
char readc;
void read(int &n){
    while((readc=getchar())<48||readc>57);
    n=readc-48;
    while((readc=getchar())>=48&&readc<=57) n=n*10+readc-48;
}
int get(int x){
    return ch[fa[x]][1]==x;
}
bool isroot(int x){
    return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}
void pushup(int x){
    int tmp=x;
    if(val[tmp]<val[maxid[ch[x][0]]]) tmp=maxid[ch[x][0]];
    if(val[tmp]<val[maxid[ch[x][1]]]) tmp=maxid[ch[x][1]];
    maxid[x]=tmp;
}
void pushdown(int x){
    if(rev[x]){
        swap(ch[x][0],ch[x][1]);
        rev[ch[x][0]]^=1,rev[ch[x][1]]^=1;
        rev[x]=false;
    }
}
void rotate(int x){
    int y=fa[x],z=fa[y],k=get(x),w=ch[x][k^1];
    ch[y][k]=w,fa[w]=y;
    if(!isroot(y)) ch[z][get(y)]=x;
    fa[x]=z;
    ch[x][k^1]=y,fa[y]=x;
    pushup(y),pushup(x);
}
void splay(int x){
    q[qtop=1]=x;
    for(int i=x;!isroot(i);i=fa[i]) q[++qtop]=fa[i];
    for(int i=qtop;i>=1;i--) pushdown(q[i]);
    while(!isroot(x)){
        int y=fa[x];
        if(!isroot(y)){
            if(get(x)==get(y)) rotate(y); else rotate(x);
        }
        rotate(x);
    }
    pushup(x);
}
void access(int x){
    for(int y=0;x;y=x,x=fa[x]){
        splay(x),ch[x][1]=y;
        pushup(x);
    }
}
void makeroot(int x){
    access(x);
    splay(x);
    rev[x]^=1;
}
void split(int x,int y){
    makeroot(x);
    access(y);
    splay(y);
}
int findroot(int x){
    access(x);
    splay(x);
    while(ch[x][0]) pushdown(x),x=ch[x][0];
    return x;
}
void link(int x,int y){
    makeroot(x);
    if(findroot(y)!=x) fa[x]=y;
}
void cut(int x,int y){
    split(x,y);
    if(findroot(y)==x&&fa[x]==y&&ch[x][1]==0){
        fa[x]=0;
        ch[y][0]=0;
        pushup(y);
    }
}
void update(int id){
    int x=p[id].u,y=p[id].v;
    if(x==y) return;
    makeroot(x);
    if(findroot(y)!=x){
        val[++ncnt]=p[id].b;
        link(x,ncnt);
        linkline[ncnt][0]=x;
        link(y,ncnt);
        linkline[ncnt][1]=y;
    }else{
        split(x,y);
        int tmp=maxid[y];
        if(p[id].b<val[tmp]){
            cut(linkline[tmp][0],tmp);
            cut(linkline[tmp][1],tmp);
            val[tmp]=p[id].b;
            link(x,tmp);
            link(y,tmp);
            linkline[tmp][0]=x,linkline[tmp][1]=y;
        }
    }
}
int query(){
    if(findroot(1)!=findroot(n)) return 1000000000;
    split(1,n);
    return val[maxid[n]];
}
int main(){
    ans=1000000000;
    read(n),read(m);
    for(i=1;i<=m;i++){
        read(x),read(y),read(a),read(b);
        if(x>y) swap(x,y);
        p[i]=Node(x,y,a,b);
    }
    ncnt=n;
    sort(p+1,p+m+1);
    for(i=1;i<=m;i++){
        update(i);
        ans=min(ans,query()+p[i].a);
    }
    if(ans==1000000000){
        printf("-1\n");
    }else{
        printf("%d\n",ans);
    }
    return 0;
}

洛谷P2387 [NOI2014]魔法森林