1. 程式人生 > >[BZOJ3669]魔法森林

[BZOJ3669]魔法森林

含義 script 書法 最大的 組成 得到 bool 就會 無法

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個守護精靈。
綜上所述,小E最少需要攜帶32個守護精靈。

【輸出樣例2】

-1
【樣例說明2】
小E無法從1號節點到達3號節點,故輸出-1。

HINT

2<=n<=50,000

0<=m<=100,000

1<=ai ,bi<=50,000

Source

首先有一個很簡單的想法,假如我們按$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]魔法森林