传送门

题目大意

给定 \(n\) 个点和 \(m\) 条边。每条边包含起点终点和两个精灵的最低限制,求最少需要携带的精灵数量。

题目解析

直接套 LCT 板子

将所有边按照进行升序排序,从小到大将边加入,在已经加入边的图上找路径的最大值,求出最大值和当前枚举的和用于更新全局的最小值答案。

为什么呢?因为要 \(a\),\(b\) 都满足才能通过某条边,所以结果必定为某条边的 \(a_i\) 或 \(b_i\), 因此可以固定 \(a\) 的信息来降低复杂度。即每次选取小于等于 \(a_i\) 大小的边去维护一条 \(1\) 到 \(n\) 的路径.

动态加边,维护最大值。

直接套 LCT 板子!!

虽然题目给出的是一张图, 但实际上只需要维护出一条从 \(1\) 到 \(n\) 的路径即可.因此当新加入一条边会使维护的树变成图时, 就需要去找到环, 若新边比环中最大值小, 那么将环中的最大边删去, 加入新边即可。

然后要注意用并查集(好像是卡常,因为 yxc 直接用的并查集)

利用动态树的特性快速求路径上的最大点权值。最后注意一下删边时的编号映射。(关于我忘了切断子树卡了半天15pts艹)

代码实现

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. #define rint register int
  4. #define endl '\n'
  5. using namespace std;
  6. const int N = 1e6 + 5;
  7. int n, m, p[N], stk[N], ans = 0x3f3f3f3f;
  8. struct Edge
  9. {
  10. int x, y, a, b;
  11. bool operator<(const Edge &t) const { return a < t.a; }
  12. } e[N];
  13. struct node
  14. {
  15. int s[2], p, v, mx, rev;
  16. } tr[N];
  17. int inline min(int a, int b)
  18. {
  19. return a < b ? a : b;
  20. }
  21. int inline find(int x)
  22. {
  23. if (p[x] != x)
  24. p[x] = find(p[x]);
  25. return p[x];
  26. }
  27. void inline pushrev(int u)
  28. {
  29. swap(tr[u].s[0], tr[u].s[1]);
  30. tr[u].rev ^= 1;
  31. return;
  32. }
  33. void inline pushup(int u)
  34. {
  35. tr[u].mx = u;
  36. int ll = tr[tr[u].s[0]].mx;
  37. int rr = tr[tr[u].s[1]].mx;
  38. if (tr[ll].v > tr[tr[u].mx].v)
  39. {
  40. tr[u].mx = ll;
  41. }
  42. if (tr[rr].v > tr[tr[u].mx].v)
  43. {
  44. tr[u].mx = rr;
  45. }
  46. return;
  47. }
  48. void inline pushdown(int u)
  49. {
  50. if (tr[u].rev)
  51. {
  52. pushrev(tr[u].s[0]);
  53. pushrev(tr[u].s[1]);
  54. tr[u].rev = 0;
  55. }
  56. return;
  57. }
  58. bool inline isroot(int u)
  59. {
  60. return tr[tr[u].p].s[0] != u && tr[tr[u].p].s[1] != u;
  61. }
  62. void inline rotate(int x)
  63. {
  64. int y = tr[x].p;
  65. int z = tr[y].p;
  66. int k = tr[y].s[1] == x;
  67. if (!isroot(y))
  68. {
  69. tr[z].s[tr[z].s[1] == y] = x;
  70. }
  71. tr[x].p = z;
  72. tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
  73. tr[x].s[k ^ 1] = y, tr[y].p = x;
  74. pushup(y);
  75. pushup(x);
  76. return;
  77. }
  78. void inline splay(int x)
  79. {
  80. int top = 0, r = x;
  81. stk[++top] = r;
  82. while (!isroot(r))
  83. {
  84. stk[++top] = r = tr[r].p;
  85. }
  86. while (top)
  87. {
  88. pushdown(stk[top--]);
  89. }
  90. while (!isroot(x))
  91. {
  92. int y = tr[x].p, z = tr[y].p;
  93. if (!isroot(y))
  94. {
  95. if ((tr[z].s[1] == y) ^ (tr[y].s[1] == x))
  96. rotate(x);
  97. else
  98. rotate(y);
  99. }
  100. rotate(x);
  101. }
  102. return;
  103. }
  104. void inline access(int x)
  105. {
  106. int z = x;
  107. for (rint y = 0; x; y = x, x = tr[y].p)
  108. {
  109. splay(x);
  110. tr[x].s[1] = y, pushup(x);
  111. }
  112. splay(z);
  113. return;
  114. }
  115. void inline makeroot(int x)
  116. {
  117. access(x);
  118. pushrev(x);
  119. return;
  120. }
  121. int inline findroot(int x)
  122. {
  123. access(x);
  124. while (tr[x].s[0])
  125. {
  126. pushdown(x);
  127. x = tr[x].s[0];
  128. }
  129. splay(x);
  130. return x;
  131. }
  132. void inline split(int x, int y)
  133. {
  134. makeroot(x);
  135. access(y);
  136. return;
  137. }
  138. void inline link(int x, int y)
  139. {
  140. makeroot(x);
  141. if (findroot(y) != x)
  142. tr[x].p = y;
  143. return;
  144. }
  145. void inline cut(int x, int y)
  146. {
  147. makeroot(x);
  148. if (findroot(y) == x && tr[x].s[1] == y && !tr[y].s[0])
  149. {
  150. tr[y].p = tr[x].s[1] = 0;
  151. pushup(x);
  152. }
  153. return;
  154. }
  155. signed main()
  156. {
  157. cin >> n >> m;
  158. for (rint i = 1; i <= m; i++)
  159. {
  160. int x, y, a, b;
  161. cin >> x >> y >> a >> b;
  162. e[i] = {x, y, a, b};
  163. }
  164. sort(e + 1, e + 1 + m);
  165. for (rint i = 1; i <= n + m; i++)
  166. {
  167. p[i] = i;
  168. if (i > n)
  169. tr[i].v = e[i - n].b;
  170. tr[i].mx = i;
  171. }
  172. for (rint i = 1; i <= m; i++)
  173. {
  174. int x = e[i].x;
  175. int y = e[i].y;
  176. int a = e[i].a;
  177. int b = e[i].b;
  178. if (find(x) == find(y))
  179. {
  180. split(x, y);
  181. int t = tr[y].mx;
  182. if (tr[t].v > b)
  183. {
  184. cut(t, e[t - n].x);
  185. cut(t, e[t - n].y);
  186. link(i + n, x);
  187. link(i + n, y);
  188. }
  189. }
  190. else
  191. {
  192. p[find(x)] = find(y);
  193. link(i + n, x);
  194. link(i + n, y);
  195. }
  196. if (find(1) == find(n))
  197. {
  198. split(1, n);
  199. ans = min(ans, a + tr[tr[n].mx].v);
  200. }
  201. }
  202. if (ans != 0x3f3f3f3f)
  203. {
  204. cout << ans << endl;
  205. return 0;
  206. }
  207. puts("-1");
  208. return 0;
  209. }

【NOI2014】 魔法森林---解题报告的更多相关文章

  1. 洛谷 P2387 [NOI2014]魔法森林 解题报告

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

  2. NOI2014魔法森林题解报告

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

  3. NOI2014 魔法森林

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

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

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

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

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

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

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

  7. BZOJ_3669_[Noi2014]魔法森林_LCT

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

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

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

  9. 「luogu2387」[NOI2014] 魔法森林

    「luogu2387」[NOI2014] 魔法森林 题目大意 \(n\) 个点 \(m\) 条边的无向图,每条边上有两个权值 \(a,b\),求从 \(1\) 节点到 \(n\) 节点 \(max\{ ...

  10. P2387 [NOI2014]魔法森林(LCT)

    P2387 [NOI2014]魔法森林 LCT边权维护经典题 咋维护呢?边化为点,边权变点权. 本题中我们把边对关键字A进行排序,动态维护关键字B的最小生成树 加边后出现环咋办? splay维护最大边 ...

随机推荐

  1. IIC、SPI、UART三者对比

    下面将对比三者的各自差异: 参考资料: 1.(112条消息) UART, SPI, IIC的详解及三者的区别和联系_iic spi uart_静思心远的博客-CSDN博客

  2. 王道oj/problem20

    网址:oj.lgwenda.com/problem/20 思路:层次建树,用递归的方法前序遍历 代码: #define _CRT_SECURE_NO_WARNINGS#include <stdi ...

  3. 【python】调用钉钉机器人发起通知

    有时候需要做个某些服务的状态监控,用钉钉机器人发通知挺方便的.可以用shell脚本配合crontab检测状态,检测到异常就调用python脚本发起告警. python内容 此处用的python3,需要 ...

  4. 10、Spring之AOP概述

    10.1.概念 AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程 AOP是面向对象编程(OOP)的一种补充和完善,OOP是纵向继承机制,A ...

  5. Unity TextMeshPro 添加中文字体遇见的问题以及解决方案

    前言 按标准官方教程为 Unity TextMeshPro 添加中文字体时出现了各种奇奇怪怪的问题,于是有了这篇随笔. 中文字体解决方案 以下步骤适用于 TextMeshPro 3.0.6. 字符数量 ...

  6. 【matplotlib基础】--子图

    使用Matplotlib对分析结果可视化时,比较各类分析结果是常见的场景.在这类场景之下,将多个分析结果绘制在一张图上,可以帮助用户方便地组合和分析多个数据集,提高数据可视化的效率和准确性. 本篇介绍 ...

  7. 【译】.NET 8 拦截器(interceptor)

    通常情况下,出于多种原因,我不会说我喜欢写关于预览功能的文章.我的大多数帖子旨在帮助人们解决他们可能遇到的问题,而不是找个肥皂盒或打广告.但是我认为我应该介绍这个 .NET 预览特性,因为它是我在 . ...

  8. Prompt 指北:如何写好 Prompt,让 GPT 的回答更加精准

    目录 1. 得亏 GPT 脾气好 2. 玩 GPT 得注意姿势 3. 指南指北指东指西 3.1 首先你得理解 GPT 是咋工作的 3.2 "Prompt 工程"走起 3.3 奇淫技 ...

  9. 如何get一个终身免费续期的定制数字人?

    想拥有一个"数字分身" 吗?给你一个终身免费续期的特权. 定制周期长?训练.运营成本高?成片效果生硬?无法应用于实际场景? 随着AIGC技术的快速发展,虚拟数字人的生成效率不断提高 ...

  10. BZ全景可视化编辑器 (KRPano可视化编辑器, 无需编写任何代码制作全景漫游)

    软件简介 BZ全景编辑器是一款KRPano全景可视化编辑工具,下载安装即可使用,无需拥有任何KRPano代码基础,便可以制作生成精美的全景漫游作品. 官方网站: 点击进入官方网站 最新版软件下载地址: ...