[BZOJ3669]魔法森林
Description
為了得到書法大家的真傳,小E同學下定決心去拜訪住在魔法森林中的隱士。魔法森林可以被看成一個包含個N節點M條邊的無向圖,節點標號為1..N,邊標號為1..M。初始時小E同學在號節點1,隱士則住在號節點N。小E需要通過這一片魔法森林,才能夠拜訪到隱士。
魔法森林中居住了一些妖怪。每當有人經過一條邊的時候,這條邊上的妖怪就會對其發起攻擊。幸運的是,在號節點住著兩種守護精靈:A型守護精靈與B型守護精靈。小E可以借助它們的力量,達到自己的目的。
只要小E帶上足夠多的守護精靈,妖怪們就不會發起攻擊了。具體來說,無向圖中的每一條邊Ei包含兩個權值Ai與Bi。若身上攜帶的A型守護精靈個數不少於Ai,且B型守護精靈個數不少於Bi,這條邊上的妖怪就不會對通過這條邊的人發起攻擊。當且僅當通過這片魔法森林的過程中沒有任意一條邊的妖怪向小E發起攻擊,他才能成功找到隱士。
由於攜帶守護精靈是一件非常麻煩的事,小E想要知道,要能夠成功拜訪到隱士,最少需要攜帶守護精靈的總個數。守護精靈的總個數為A型守護精靈的個數與B型守護精靈的個數之和。
Input
第1行包含兩個整數N,M,表示無向圖共有N個節點,M條邊。 接下來M行,第行包含4個正整數Xi,Yi,Ai,Bi,描述第i條無向邊。其中Xi與Yi為該邊兩個端點的標號,Ai與Bi的含義如題所述。 註意數據中可能包含重邊與自環。
Output
輸出一行一個整數:如果小E可以成功拜訪到隱士,輸出小E最少需要攜帶的守護精靈的總個數;如果無論如何小E都無法拜訪到隱士,輸出“-1”(不含引號)。
Sample Input
【輸入樣例1】4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
【輸入樣例2】
3 1
1 2 1 1
Sample Output
【輸出樣例1】32
【樣例說明1】
如果小E走路徑1→2→4,需要攜帶19+15=34個守護精靈;
如果小E走路徑1→3→4,需要攜帶17+17=34個守護精靈;
如果小E走路徑1→2→3→4,需要攜帶19+17=36個守護精靈;
如果小E走路徑1→3→2→4,需要攜帶17+15=32個守護精靈。
【輸出樣例2】
-1
【樣例說明2】
小E無法從1號節點到達3號節點,故輸出-1。
HINT
2<=n<=50,000
0<=m<=100,000
1<=ai ,bi<=50,000Source
首先有一個很簡單的想法,假如我們按$a$值從小到大排序,那麽隨著$a$值的增大,為了保證答案最優,$b$值要相應的減小才行。所以我們可以用$LCT$維護這個過程,當加入一條新邊時,若組成環且環上最大的$b$要大於當前邊,那麽就用這條邊替換掉環上的邊即可。對於用$LCT$維護邊的信息有一個小技巧,就是對於每條邊新建一個點,點權等於邊權,向兩個端點連邊
代碼:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #define M 300010 5 #define ls ch[x][0] 6 #define rs ch[x][1] 7 using namespace std; 8 struct point{int u,v,a,b;}e[M]; 9 int n,m,ans=1e9; 10 int fa[M],f[M],maxn[M],val[M],id[M],q[M],rev[M],ch[M][2]; 11 int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);} 12 bool cmp(point a1,point a2) {return a1.a<a2.a;} 13 void pushdown(int x){if(rev[x]){rev[ls]^=1,rev[rs]^=1;swap(ls,rs),rev[x]^=1;}} 14 void update(int x) 15 { 16 maxn[x]=val[x],id[x]=x; 17 if(maxn[ls]>maxn[x]) maxn[x]=maxn[ls],id[x]=id[ls]; 18 if(maxn[rs]>maxn[x]) maxn[x]=maxn[rs],id[x]=id[rs]; 19 } 20 int get(int x) {return ch[f[x]][1]==x;} 21 int is_root(int x) {return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;} 22 void rotate(int x) 23 { 24 int old=f[x],oldf=f[old],k=get(x); 25 if(!is_root(old)) ch[oldf][ch[oldf][1]==old]=x; 26 ch[old][k]=ch[x][k^1],f[ch[old][k]]=old; 27 ch[x][k^1]=old,f[old]=x,f[x]=oldf; 28 update(old),update(x); 29 } 30 void splay(int x) 31 { 32 int top=1,fa;q[top]=x; 33 for(int i=x;!is_root(i);i=f[i]) q[++top]=f[i]; 34 for(int i=top;i;i--) pushdown(q[i]); 35 while(!is_root(x)) 36 { 37 if(!is_root(fa=f[x])) 38 rotate(get(x)==get(fa)?fa:x); 39 rotate(x); 40 } 41 } 42 void access(int x) 43 { 44 for(int y=0;x;y=x,x=f[x]) 45 splay(x),ch[x][1]=y,update(x); 46 } 47 void makeroot(int x) {access(x),splay(x),rev[x]^=1;} 48 void spilt(int x,int y) {makeroot(x);access(y);splay(y);} 49 int query(int x,int y) {spilt(x,y);return id[y];} 50 void link(int x,int y) {makeroot(x);f[x]=y;splay(x);} 51 void cut(int x,int y) {spilt(x,y);ch[y][0]=f[x]=0;} 52 int main() 53 { 54 scanf("%d%d",&n,&m); 55 for(int i=1;i<=m;i++) 56 { 57 int x,y,a,b;scanf("%d%d%d%d",&x,&y,&a,&b); 58 e[i]=(point){x,y,a,b}; 59 } 60 sort(e+1,e+1+m,cmp); 61 for(int i=1;i<=n+m;i++) fa[i]=i,id[i]=i; 62 for(int i=1;i<=m;i++) val[i+n]=e[i].b; 63 for(int i=1;i<=m;i++) 64 { 65 int x=e[i].u,y=e[i].v;bool flag=true; 66 if(find(x)==find(y)) 67 { 68 int id=query(x,y); 69 if(val[id]>e[i].b) 70 cut(e[id-n].u,id),cut(e[id-n].v,id); 71 else flag=false; 72 } 73 else fa[find(x)]=find(y); 74 if(flag) link(x,i+n),link(y,i+n); 75 if(find(1)==find(n)) ans=min(ans,e[i].a+val[query(1,n)]); 76 } 77 if(find(1)!=find(n)) puts("-1"); 78 else printf("%d\n",ans); 79 return 0; 80 }
[BZOJ3669]魔法森林