题目描述

化学家吉丽想要配置一种神奇的药水来拯救世界。
吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号)。初始时,第i个瓶内装着g[i]克的第i种物质。吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到。瓶子的容量可以视作是无限的。
吉丽知道某几对液体物质在一起时会发生反应产生沉淀,具体反应是1克c[i]物质和1克d[i]物质生成2克沉淀,一直进行直到某一反应物耗尽。生成的沉淀不会和任何物质反应。当有多于一对可以发生反应的物质在一起时,吉丽知道它们的反应顺序。每次倾倒完后,吉丽会等到反应结束后再执行下一步骤。
吉丽想知道配置过程中总共产生多少沉淀。

输入

第一行三个整数n,m,k(0<=m<n<=200000,0<=k<=500000),分别表示药瓶的个数(即物质的种数),操作步数,可以发生的反应数量。
第二行有n个整数g[1],g[2],…,g[n](1<=g[i]<=10^9),表示初始时每个瓶内物质的质量。
接下来m行,每行两个整数a[i],b[i](1<=a[i],b[i]<=n,a[i]≠b[i]),表示第i个步骤。保证a[i]在以后的步骤中不再出现。
接下来k行,每行是一对可以发生反应的物质c[i],d[i](1<=c[i],d[i]<=n,c[i]≠d[i]),按照反应的优先顺序给出。同一个反应不会重复出现。

输出

样例输入

3 2 1
2 3 4
1 2
3 2
2 3

样例输出

6
 
我们将每瓶药看成一个节点,对于一个操作合并x,y两瓶药就再新建一个节点代表这个操作,左右子节点分别是x,y两瓶药所在子树的根节点。
这样像kruskal重构树一样建出一棵二叉树,也叫并查集重构树。
可以发现对于每一对反应的两瓶药x,y,都是在它们在并查集重构树上的LCA处那个操作时发生反应的。
我们以每个反应的LCA深度为第一关键字,优先度为第二关键字排序,然后模拟一下即可。

  1. #include<set>
  2. #include<map>
  3. #include<cmath>
  4. #include<stack>
  5. #include<queue>
  6. #include<vector>
  7. #include<bitset>
  8. #include<cstdio>
  9. #include<cstring>
  10. #include<iostream>
  11. #include<algorithm>
  12. #define ll long long
  13. using namespace std;
  14. int n,m,k;
  15. int g[200010];
  16. int fa[400010];
  17. int vis[400010];
  18. ll ans;
  19. int f[400010][20];
  20. int d[400010];
  21. int cnt;
  22. int tot;
  23. int ls[400010];
  24. int rs[400010];
  25. struct miku
  26. {
  27. int dep;
  28. int pos;
  29. int x,y;
  30. }a[500010];
  31. int x,y;
  32. int find(int x)
  33. {
  34. if(fa[x]==x)
  35. {
  36. return x;
  37. }
  38. return fa[x]=find(fa[x]);
  39. }
  40. bool cmp(miku a,miku b)
  41. {
  42. if(a.dep!=b.dep)
  43. {
  44. return a.dep>b.dep;
  45. }
  46. return a.pos<b.pos;
  47. }
  48. void dfs(int x)
  49. {
  50. d[x]=d[f[x][0]]+1;
  51. for(int i=1;i<=18;i++)
  52. {
  53. f[x][i]=f[f[x][i-1]][i-1];
  54. }
  55. if(ls[x])
  56. {
  57. dfs(ls[x]);
  58. }
  59. if(rs[x])
  60. {
  61. dfs(rs[x]);
  62. }
  63. }
  64. int lca(int x,int y)
  65. {
  66. if(d[x]<d[y])
  67. {
  68. swap(x,y);
  69. }
  70. int dep=d[x]-d[y];
  71. for(int i=0;i<=18;i++)
  72. {
  73. if((dep&(1<<i)))
  74. {
  75. x=f[x][i];
  76. }
  77. }
  78. if(x==y)
  79. {
  80. return x;
  81. }
  82. for(int i=18;i>=0;i--)
  83. {
  84. if(f[x][i]!=f[y][i])
  85. {
  86. x=f[x][i];
  87. y=f[y][i];
  88. }
  89. }
  90. return f[x][0];
  91. }
  92. int main()
  93. {
  94. scanf("%d%d%d",&n,&m,&k);
  95. for(int i=1;i<=n;i++)
  96. {
  97. scanf("%d",&g[i]);
  98. }
  99. for(int i=1;i<=2*n;i++)
  100. {
  101. fa[i]=i;
  102. }
  103. cnt=n;
  104. for(int i=1;i<=m;i++)
  105. {
  106. scanf("%d%d",&x,&y);
  107. int fx=find(x);
  108. int fy=find(y);
  109. cnt++;
  110. f[fx][0]=cnt;
  111. f[fy][0]=cnt;
  112. fa[fx]=cnt;
  113. fa[fy]=cnt;
  114. ls[cnt]=fx;
  115. rs[cnt]=fy;
  116. }
  117. for(int i=1;i<=n;i++)
  118. {
  119. int rt=find(i);
  120. if(!vis[rt])
  121. {
  122. vis[rt]=1;
  123. dfs(rt);
  124. }
  125. }
  126. for(int i=1;i<=k;i++)
  127. {
  128. scanf("%d%d",&x,&y);
  129. if(find(x)==find(y))
  130. {
  131. a[++tot].dep=d[lca(x,y)];
  132. a[tot].pos=i;
  133. a[tot].x=x;
  134. a[tot].y=y;
  135. }
  136. }
  137. sort(a+1,a+1+tot,cmp);
  138. for(int i=1;i<=tot;i++)
  139. {
  140. int s=g[a[i].x];
  141. int t=g[a[i].y];
  142. if(s>t)
  143. {
  144. swap(s,t);
  145. }
  146. ans+=2ll*s;
  147. g[a[i].x]-=s;
  148. g[a[i].y]-=s;
  149. }
  150. printf("%lld",ans);
  151. }

BZOJ3712[PA2014]Fiolki——并查集重构树的更多相关文章

  1. 【BZOJ3712】Fiolki(并查集重构树)

    [BZOJ3712]Fiolki(并查集重构树) 题面 BZOJ 题解 很神仙的题目. 我们发现所有的合并关系构成了一棵树. 那么两种不同的东西如果产生反应,一定在两个联通块恰好联通的时候反应. 那么 ...

  2. [学习笔记]kruskal重构树 && 并查集重构树

    Kruskal 重构树 [您有新的未分配科技点][BZOJ3545&BZOJ3551]克鲁斯卡尔重构树 kruskal是一个性质优秀的算法 加入的边是越来越劣的 科学家们借这个特点尝试搞一点事 ...

  3. [bzoj3712][PA2014]Fiolki

    description 题面 data range \[ 0\le m<n\le 200000,0\le k\le 500000\] solution 之前本人一直煞笔地思考暴力是否可行 考虑按 ...

  4. BZOJ3712[PA2014]Fiolki 建图+倍增lca

    居然是一道图论题 毫无思路 我们对于每一次的融合操作 $(a,b)$ 建一个新点$c$ 并向$a,b$连边 再将$b$瓶当前的位置赋成$c$ 这样子我们就可以建成一个森林 现在枚举每一种反应$M_i$ ...

  5. [note]克鲁斯卡尔重构树

    克鲁斯卡尔重构树 又叫并查集重构树 大概在NOI2018之前还是黑科技 现在?烂大街了 主要是针对图上的对边有限制的一类问题 比如每次询问一个点u不能经过边权大于w的边能走到的第k大点权是多少 也就是 ...

  6. [BZOJ3712]Fiolki 重构树(并查集)

    3712: [PA2014]Fiolki Time Limit: 30 Sec  Memory Limit: 128 MB Description 化学家吉丽想要配置一种神奇的药水来拯救世界.吉丽有n ...

  7. NOI2018Day1T1 归程 并查集 kruskal kruskal重构树 倍增表 Dijkstra

    原文链接https://www.cnblogs.com/zhouzhendong/p/NOI2018Day1T1.html 题目传送门 - 洛谷P4768 题意 给定一个无向连通图,有 $n$ 个点 ...

  8. [NOI2018]归程(可持久化并查集,Kruskal重构树)

    解法一: 1.首先想到离线做法:将边和询问从大到小排序,并查集维护连通块以及每个连通块中所有点到1号点的最短距离.$O(n\log n)$ 配合暴力等可以拿到75分. 2.很容易想到在线做法,使用可持 ...

  9. CF1253F Cheap Robot(神奇思路,图论,最短路,最小生成树/Kruskal 重构树/并查集)

    神仙题. 先考虑平方级别的暴力怎么做. 明显答案有单调性,先二分 \(c\). 先最短路预处理 \(dis_u\) 表示 \(u\) 到离它最近的充电站的距离(一开始把 \(1\) 到 \(k\) 全 ...

随机推荐

  1. 利用 ProtoThreads实现Arduino多线程处理(1)

    转载请注明:@小五义http://www.cnblogs.com/xiaowuyiQQ群:64770604 这几天和群里小V同学讨论一个项目时,偶然发现了 ProtoThreads,简称PT,用其来实 ...

  2. 覆写equals方法为什么需要覆写hashCode方法

    覆写equals方法必须覆写hashCode方法,是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢? void test() { // Person类的实例作为Map的k ...

  3. Ionic Contoller类与Service类分开需要注意的问题

    看了别人的项目把Controller类和Service类都写在了app.js文件里面,这不符合我的风格,想把他们分开成单独的文件,确遇见以下错误提示: ionic.bundle.min.js:133 ...

  4. LInq之Take Skip TakeWhile SkipWhile Reverse Union Concat 用法

    废话不多说,直接上代码,代码有注释!自行运行测试! class Program { static void Main(string[] args) { string[] names = { " ...

  5. 51Nod 1705 七星剑

    一道很新颖的概率DP,我看数据范围还以为是有指数级别的复杂度的呢 记得有人说期望要倒着推,但放在这道题上,就咕咕了吧. 我们考虑正着概率DP,设\(fi\)表示将剑升到\(i\)颗星花费的期望,这样我 ...

  6. Python中 and or 运算顺序详解 --- 短路逻辑

    核心思想 表达式从左至右运算,若 or 的左侧逻辑值为 True ,则短路 or 后所有的表达式(不管是 and 还是 or),直接输出 or 左侧表达式 . 表达式从左至右运算,若 and 的左侧逻 ...

  7. python基础学习笔记(十)

    魔法方法.属性 ------------------------ 准备工作 为了确保类是新型类,应该把 _metaclass_=type 入到你的模块的最开始. class NewType(Objec ...

  8. [个人博客作业Week7]软件工程团队项目感想与反思

    在阅读了推荐阅读的材料之后,我想了很多东西.最终还是决定,以团队项目的经历为主线,叙述我关于软件工程的一些思考与体会. 凤凰涅槃,浴火重生 如果要我来概况这几周团队项目的经历的话,那么句话是我所能想到 ...

  9. 【实践报告】Linux实践四

    Linux内核分析 实践四——ELF文件格式分析 一.概述 1.ELF全称Executable and Linkable Format,可执行连接格式,ELF格式的文件用于存储Linux程序.ELF文 ...

  10. 《面向对象程序设计》c++第五次作业___calculator plus plus

    c++第五次作业 Calculator plusplus 代码传送门 PS:这次作业仍然orz感谢一位同学与一位学长的windows帮助,同时再次吐槽作业对Mac系统用户的不友好.(没朋友千万别用Ma ...