【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\n",ans); 172 else printf("-1\n"); 173 return 0; 174 }
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
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
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