【BZOJ3669】[Noi2014]魔法森林 LCT

  终于不是裸的LCT了。。。然而一开始一眼看上去这是kruskal。。不对,题目要求1->n的路径上的每个点的两个最大权值和最小,这样便可以用LCT来维护一个最小生成路(瞎编的。。。),先以a为关键字排序,然后加边,所以每次加入一条边时a一定是最大的,考虑b的大小,当形成环时,考虑用当前边替换掉环内b最大的边,当然是当前边b小于权值最大的边拉!

  Tips:1.注意LCT要把每条边也作为一个节点,方便连接,然后每个节点维护所在边在边集里的编号即可。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #define N 50050
  5 #define M 100100
  6 #define inf 0x7fffffff
  7 using namespace std;
  8 int n,m,ans; 
  9 struct E{int x,y,a,b;}e[M];
 10 struct node
 11 {
 12     node *fa,*ch[2];
 13     void init();
 14     int pos,rpos;
 15     bool rev;
 16     bool chr() {return this==fa->ch[1];}
 17     bool isrt() {return this!=fa->ch[1] && this!=fa->ch[0];}
 18     void setc(node *x,int t) {this->ch[t]=x; x->fa=this;}
 19     void push_up();
 20     void push_down();
 21 /*    void push_down()
 22     {
 23         if (this==null) return;
 24         if (!x->isrt()) x->fa->push_down();
 25         if (rev)
 26         {
 27             ch[1]->rev^=1;
 28             ch[0]->rev^=1;
 29             swap(ch[1],ch[0]);
 30             rev=0;
 31         }
 32     }
 33     void push_up()
 34     {
 35         if (this==null) return;
 36         pos=rpos;
 37         if (ch[0]!=null && e[pos].b<e[ch[0]->pos].b)    pos=ch[0]->pos;
 38         if (ch[1]!=null && e[pos].b<e[ch[1]->pos].b)    pos=ch[1]->pos;
 39     }*/
 40 }pool[N+M],*pt=pool,*point[N],*et[M],*null;
 41 void node::init() {ch[0]=ch[1]=fa=null; pos=0; rev=0; rpos=0;}
 42 void node::push_up()
 43 {
 44     if (this==null) return;
 45     pos=rpos;
 46     if (ch[0]!=null && e[pos].b<e[ch[0]->pos].b)    pos=ch[0]->pos;
 47     if (ch[1]!=null && e[pos].b<e[ch[1]->pos].b)    pos=ch[1]->pos;
 48 } 
 49 void node::push_down()
 50 {
 51     if (this==null) return;
 52         if (!this->isrt()) this->fa->push_down();
 53         if (rev)
 54         {
 55             if (ch[1]!=null) ch[1]->rev^=1;
 56             if (ch[0]!=null) ch[0]->rev^=1;
 57             swap(ch[1],ch[0]);
 58             rev=0;
 59         }
 60 }
 61 namespace LCT
 62 {
 63     node *NewNode()
 64     {
 65         pt++;
 66         pt->init();
 67         return pt;
 68     }
 69     void rotate(node *x)
 70     {
 71         node *r=x->fa;
 72         if (x==null || r==null) return;
 73         int t=x->chr();
 74         if (r->isrt()) x->fa=r->fa;
 75         else r->fa->setc(x,r->chr());
 76         r->setc(x->ch[t^1],t);
 77         x->setc(r,!t);
 78         x->push_up(); r->push_up();
 79     }
 80     void Splay(node *x)
 81     {
 82         x->push_down();
 83         for (;!x->isrt();rotate(x))
 84             if (!x->fa->isrt())
 85                 if (x->chr()==x->fa->chr()) rotate(x->fa);
 86                 else rotate(x);
 87         x->push_up();
 88     }
 89     void Access(node *x)
 90     {
 91         node *r=null;
 92         for (;x!=null;r=x,x=x->fa)
 93         {
 94             Splay(x);
 95             x->ch[1]=r;
 96         }
 97     }
 98     void MoveRoot(node *x)
 99     {
100         Access(x);
101         Splay(x);
102         x->rev^=1;
103     }
104     void Link(node *x,node *y)
105     {
106         MoveRoot(x);
107         x->fa=y;
108     }
109     void Cut(node *x,node *y)
110     {
111         MoveRoot(x);
112         Access(y); Splay(y);
113         y->ch[0]->fa=null; y->ch[0]=null;
114     }
115     node *Find(node *x)
116     {
117         Access(x);
118         Splay(x);
119         while (x->ch[0]!=null) x=x->ch[0];
120         return x;
121     }
122     int Query(node *x,node *y)
123     {
124         MoveRoot(x);
125         Access(y);Splay(y);
126         return y->pos;
127     }
128 }
129 inline int read()
130 {
131     char c;
132     int ans=0,f=1;
133     while (!isdigit(c=getchar())) if (c=='-') f=-1;
134     ans=c-'0';
135     while (isdigit(c=getchar())) ans=ans*10+c-'0';
136     return ans*f;
137 }
138 inline bool cmp (E a,E b) {return a.a<b.a;}
139 using namespace LCT;
140 int main()
141 {
142     n=read();m=read();
143     null=pt++;
144     null->init();
145     for (int i=1;i<=m;i++) {e[i].x=read(); e[i].y=read(); e[i].a=read(); e[i].b=read();}
146     for (int i=1;i<=n;i++)     point[i]=NewNode();
147     sort(e+1,e+m+1,cmp);
148     ans=inf;e[0].b=-inf;
149     for (int i=1;i<=m;i++)
150     {
151         int x=e[i].x,y=e[i].y,b=e[i].b;
152         et[i]=NewNode(); et[i]->pos=et[i]->rpos=i;
153         if (Find(point[x])==Find(point[y]))
154         {
155             int pos=Query(point[x],point[y]);
156             if (e[pos].b>b)
157             {
158                 Cut(et[pos],point[e[pos].x]);
159                 Cut(et[pos],point[e[pos].y]);
160                 Link(et[i],point[x]);
161                 Link(et[i],point[y]);
162             }
163         }
164         else 
165         {
166             Link(et[i],point[x]);
167             Link(et[i],point[y]);
168         }
169         if (Find(point[1])==Find(point[n])) ans=min(ans,e[i].a+e[Query(point[1],point[n])].b);
170     }
171     if (ans!=inf) printf("%d
",ans);
172     else printf("-1
");
173     return 0; 
174 }
View Code

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

—Anime Otaku Save The World.
原文地址:https://www.cnblogs.com/DMoon/p/5329845.html