题目链接:BZOJ - 3669

题目分析

如果确定了带 x 只精灵A,那么我们就是要找一条 1 到 n 的路径,满足只经过 Ai <= x 的边,而且要使经过的边中最大的 Bi 尽量小。

其实就是一个按照 Bi 建立的 MST 上 1 到 n 的路径。只能使用 Ai <= x 的边。

那么,如果我们从小到大枚举 x ,这样可以使用的边就不断增加,就是在加边的同时维护 MST ,用 LCT 来做就可以了。

如果新加入一条边 (u, v, w) ,并且原 MST 上 u 到 v 的路径中边权最大的边的边权大于 w ,那么就删掉那条边权最大的边,然后把这条新加入的边连到 MST 中。

备注:在 Cut 不够优的边时,我之前将代码写出了非常可怕的 BUG ,但是由于神奇的写法的巧合,这个 BUG 并没有造成后果,我用错误的代码 AC 了两道题..

现在下方的代码是正确的了。

代码

  1. #include <iostream>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cstdio>
  5. #include <cmath>
  6. #include <algorithm>
  7.  
  8. using namespace std;
  9.  
  10. inline void Read(int &Num)
  11. {
  12. char c = getchar();
  13. while (c < '0' || c > '9') c = getchar();
  14. Num = c - '0'; c = getchar();
  15. while (c >= '0' && c <= '9')
  16. {
  17. Num = Num * 10 + c - '0';
  18. c = getchar();
  19. }
  20. }
  21.  
  22. inline int gmax(int a, int b) {return a > b ? a : b;}
  23. inline int gmin(int a, int b) {return a < b ? a : b;}
  24.  
  25. const int MaxN = 50000 + 5, MaxM = 100000 + 5, MaxT = 150000 + 5, INF = 999999999;
  26.  
  27. int n, m, Ans;
  28. int Father[MaxT], Son[MaxT][2], V[MaxT], T[MaxT];
  29.  
  30. bool isRoot[MaxT], Rev[MaxT];
  31.  
  32. struct ES
  33. {
  34. int u, v, p, q;
  35. } E[MaxM];
  36.  
  37. inline bool Cmp(ES e1, ES e2)
  38. {
  39. return e1.p < e2.p;
  40. }
  41.  
  42. /**************************** LCT Start *******************************/
  43.  
  44. inline int Tmax(int a, int b) {return V[a] > V[b] ? a : b;}
  45.  
  46. inline void Update(int x)
  47. {
  48. T[x] = Tmax(x, Tmax(T[Son[x][0]], T[Son[x][1]]));
  49. }
  50.  
  51. inline void Reverse(int x)
  52. {
  53. Rev[x] = !Rev[x];
  54. swap(Son[x][0], Son[x][1]);
  55. }
  56.  
  57. inline void PushDown(int x)
  58. {
  59. if (!Rev[x]) return;
  60. Rev[x] = false;
  61. if (Son[x][0]) Reverse(Son[x][0]);
  62. if (Son[x][1]) Reverse(Son[x][1]);
  63. }
  64.  
  65. inline int GetDir(int x)
  66. {
  67. if (x == Son[Father[x]][0]) return 0;
  68. else return 1;
  69. }
  70.  
  71. void Rotate(int x)
  72. {
  73. int y = Father[x], f;
  74. PushDown(y); PushDown(x);
  75. f = GetDir(x) ^ 1;
  76. if (isRoot[y])
  77. {
  78. isRoot[y] = false;
  79. isRoot[x] = true;
  80. }
  81. else
  82. {
  83. if (y == Son[Father[y]][0]) Son[Father[y]][0] = x;
  84. else Son[Father[y]][1] = x;
  85. }
  86. Father[x] = Father[y];
  87. Son[y][f ^ 1] = Son[x][f];
  88. if (Son[x][f]) Father[Son[x][f]] = y;
  89. Son[x][f] = y;
  90. Father[y] = x;
  91. Update(y); Update(x);
  92. }
  93.  
  94. void Splay(int x)
  95. {
  96. int y;
  97. while (!isRoot[x])
  98. {
  99. y = Father[x];
  100. if (isRoot[y])
  101. {
  102. Rotate(x);
  103. break;
  104. }
  105. if (GetDir(y) == GetDir(x)) Rotate(y);
  106. else Rotate(x);
  107. Rotate(x);
  108. }
  109. }
  110.  
  111. int Access(int x)
  112. {
  113. int y = 0;
  114. while (x != 0)
  115. {
  116. Splay(x);
  117. PushDown(x);
  118. if (Son[x][1]) isRoot[Son[x][1]] = true;
  119. Son[x][1] = y;
  120. if (y) isRoot[y] = false;
  121. Update(x);
  122. y = x;
  123. x = Father[x];
  124. }
  125. return y;
  126. }
  127.  
  128. inline void Make_Root(int x)
  129. {
  130. int t = Access(x);
  131. Reverse(t);
  132. }
  133.  
  134. inline void Link(int x, int y)
  135. {
  136. Make_Root(x);
  137. Splay(x);
  138. Father[x] = y;
  139. }
  140.  
  141. inline void Cut(int x, int y)
  142. {
  143. Make_Root(x);
  144. Access(y);
  145. Splay(y);
  146. PushDown(y);
  147. isRoot[Son[y][0]] = true;
  148. Father[Son[y][0]] = 0;
  149. Son[y][0] = 0;
  150. Update(y);
  151. }
  152.  
  153. inline int Find_Root(int x)
  154. {
  155. int t = Access(x);
  156. while (Son[t][0] != 0) t = Son[t][0];
  157. return t;
  158. }
  159.  
  160. /**************************** LCT End *******************************/
  161.  
  162. int main()
  163. {
  164. scanf("%d%d", &n, &m);
  165. for (int i = 1; i <= m; ++i)
  166. {
  167. Read(E[i].u); Read(E[i].v);
  168. Read(E[i].p); Read(E[i].q);
  169. }
  170. sort(E + 1, E + m + 1, Cmp); // by ES.p
  171. for (int i = 1; i <= m; ++i) V[n + i] = E[i].q;
  172. for (int i = 1; i <= n + m; ++i)
  173. {
  174. isRoot[i] = true;
  175. Father[i] = 0;
  176. T[i] = i;
  177. }
  178. Ans = INF;
  179. int t, CutE;
  180. for (int i = 1; i <= m; ++i)
  181. {
  182. if (Find_Root(E[i].u) != Find_Root(E[i].v))
  183. {
  184. Link(E[i].u, n + i); Link(E[i].v, n + i);
  185. }
  186. else
  187. {
  188. Make_Root(E[i].u);
  189. t = Access(E[i].v);
  190. if (V[T[t]] > E[i].q)
  191. {
  192. CutE = T[t];
  193. Cut(CutE, E[CutE - n].u); Cut(CutE, E[CutE - n].v);
  194. Link(E[i].u, n + i); Link(E[i].v, n + i);
  195. }
  196. }
  197. if (Find_Root(1) == Find_Root(n))
  198. {
  199. Make_Root(1);
  200. t = Access(n);
  201. Ans = gmin(Ans, E[i].p + V[T[t]]);
  202. }
  203. }
  204. if (Ans == INF) Ans = -1;
  205. printf("%d\n", Ans);
  206. return 0;
  207. }

  

[BZOJ 3669] [Noi2014] 魔法森林 【LCT】的更多相关文章

  1. BZOJ 3669: [Noi2014]魔法森林( LCT )

    排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...

  2. bzoj 3669: [Noi2014]魔法森林 (LCT)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 题面: 3669: [Noi2014]魔法森林 Time Limit: 30 Sec  ...

  3. bzoj 3669: [Noi2014] 魔法森林 LCT版

    Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...

  4. BZOJ 3669: [Noi2014]魔法森林 [LCT Kruskal | SPFA]

    题目描述 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…, ...

  5. BZOJ 3669: [Noi2014]魔法森林(lct+最小生成树)

    传送门 解题思路 \(lct\)维护最小生成树.我们首先按照\(a\)排序,然后每次加入一条边,在图中维护一棵最小生成树.用并查集判断一下\(1\)与\(n\)是否联通,如果联通的话就尝试更新答案. ...

  6. bzoj 3669: [Noi2014]魔法森林

    bzoj 3669: [Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号 ...

  7. bzoj 3669: [Noi2014]魔法森林 动态树

    3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 363  Solved: 202[Submit][Status] ...

  8. bzoj 3669: [Noi2014]魔法森林 -- 动点spfa

    3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MB 动点spfa Description 为了得到书法大家的真传,小E同学下定决心 ...

  9. bzoj 3669: [Noi2014]魔法森林(并查集+LCT)

    Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...

随机推荐

  1. 【Qt】测测你对Qt的了解–Qt工具知多少

      原文:http://blog.163.com/lijiji_1515/blog/static/1268774462009103101944455/   你知道Qt都提供了哪些工具吗? 你知道Qt的 ...

  2. 编写跨平台代码之memory alignment

    编写网络包(存储在堆上)转换程序时,在hp-ux机器上运行时会遇到 si_code: 1 - BUS_ADRALN - Invalid address alignment. Please refer ...

  3. ClassLoader(摘录)

    Java虚拟机类加载过程是把Class类文件加载到内存,并对Class文件中的数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程. 在加载阶段,java虚拟机需要完成以下 ...

  4. JavaScript小笔记の经典算法等....

    1.利用toString()里面的参数,实现各进制之间的快速转换: var n = 17; binary_string = n.toString(2); //->二进制"10001&q ...

  5. display: inline-block 的神奇效果

    先上要实现的效果图: 方案一:来自卢兄: <!DOCTYPE html> <html lang="en"> <head> <meta ch ...

  6. .NET设计模式(10):装饰模式(Decorator Pattern)

      .NET设计模式(10):装饰模式(Decorator Pattern)   装饰模式(Decorator Pattern) --.NET设计模式系列之十 年月..在....对于..由于使用装饰模 ...

  7. T-SQL中的透视和逆透视

    透视 今天抽一点时间来看看透视和逆透视语句,简单的说就是行列转换.假设一个销售表中存放着产品号,产品折扣,产品价格三个列,每一种产品号可能有多种折扣,每一种折扣只对应一个产品价格.下面贴出建表语句和插 ...

  8. pat_1009

    1009. 说反话 (20) 时间限制 400 ms 内存限制 32000 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 给定一句英语,要求你编写程序,将句中 ...

  9. Node之express

    Express 是一个简洁.灵活的 node.js Web 应用开发框架, 它提供一系列强大的特性,帮助你创建各种 Web 和移动设备应用. 如何安装: npm install -g express ...

  10. ZOJ 1234 Chopsticks(动态规划)

    Chopsticks 题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=234 题目大意:给定n个筷子的长度,取k+8套筷 ...