洛谷P4768 [NOI2018]归程

LOJ#2718.「NOI2018」归程

用到 kruskal 重构树,所以先说这是个啥

显然,这和 kruskal 算法有关系 (废话

这个重构树是一个有点权的树

以最小生成树为例,当然最大也一样

先把所有原有的节点点权赋为 \(0\)

在跑 kruskal 的时候,我们没求出一条当前权值最小,且两端点不在同一集合的边时(并查集,kruskal 常规操作),我们就选这条边,然后把两端点划分在同一集合

不过上面仅仅时 kruskal 的操作,另外,我们还要在新建一个节点,把这个新节点作为父亲,这条被选择的边的两个端点,在并查集中的根,就是他的两个儿子

新建节点的点权,就是当前选的这条边的边权

然后合并集合的时候,要把新建的这个节点,和两个端点都合并在一个集合中,并且,把新建节点作为根(根说的就是并查集的最早祖先)

这样,以节点 \(x\) 为根的集合中的所有点,都在以 \(x\) 为根的树上

这样,每选择一条边,都会新建一个集合(就是新的那个节点,这个节点的集合一建立就被和其它的合并了,但是不妨还是理解为先建立),合并三个集合,也就是集合总数少了一个

那么选 \(n-1\) 条边(显然这就是最小生成树上的所有边),就会得到一个集合,这个集合的根就是重构树的根,如果从 \(n+1\) 开始为新建的节点编号,那么根的编号就是 \(2n-1\)

此时,新构建出的这个树,就是 kruskal 重构树

举个栗子,有如下一张图

它的最小生成树是这样的:

那么它的重构树是这样的

考虑一下它有哪些性质

  • 是一棵二叉树,并且每个非叶子节点都有两个儿子,观察刚才构建原则,很显然
  • 一个节点在重构树中是叶子节点,等价于这个节点是原有的节点,也很显然
  • 在树上的任意一条上下的链上(直上直下,不能在中间某个节点拐弯),如果是在求最小生成树时重构的,点权从下到上不减,如果时求最大生成树时重构的,不考虑叶子节点,是不增

    这点是很显然的,以最小生成树为例,我们总是先选择权值小的边,创建的点的点权小,然后没次新创建的点,总是在已经创建的(或者原有的)点的上面,所以一条链上总是上面的点点权更大(或相同)
  • 容易发现,最小生成树上两个点之间,树上路径中,最大边权就是重构树中,两点 LCA 的点权(最大生成树中最小边权也一样,下面就不再分类说了)
  • 通过上一条,可以知道,到节点 \(v\) 的树上路径中的最大值,小于某个值的所有节点 \(u\),都在同一个子树内

    再由第二条性质,都是这个子树内的叶子节点
  • 由上一条继续推,就能知道,这个子树的根,就是 从 \(v\) 到整个重构树的根的路径中,深度最小的,点权小于这个值的节点

    一般来说,又因为这个路径上的点圈不减,所以这个节点可以预处理一个倍增数组,来求出,下面要说的那个题就用到这个方法

基本上就是这些了,下面来看 [NOI2018]归程 这题


kruskal 重构树+倍增+dij 最短路

不算太难,当kruskal 重构树上手题比较好

话说当年应该就是这题宣布了 SPFA 的死亡(((


由于开车只能走海拔比某个值大的边,所以我们应该按照海拔,给原图求一个最大生成树的 kruskal 重构树

由上面的性质,可以 \(\log n\) 内求出所有开车能达到的节点,都在重构树中哪个节点的子树中

因为要求走路走的最短,所以用 SPFA 用 dijkstra 求出 \(1\) 到每个点的最短路(按照长度)

然后,在对重构树进行预处理 dfs 时,不光要求出倍增数组,还要求出当前节点 \(u\) 的子树中,到 \(1\) 的最短路最小的点的最短路长度

所以,用倍增跳到从起点,到整个重构树的根的路径中,深度最小的,点权大于这个值(就是水位线)的节点

然后答案就是这个点的子树中,到 \(1\) 的最短路径长度

然后,就做完了,我会做noi2018的题了,耶

自己犯的傻:

多测不清零,5分两行泪

数组没二倍(重构树有 \(2n-1\) 个节点),50分两行泪

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<iostream>
  4. #include<cmath>
  5. #include<map>
  6. #include<iomanip>
  7. #include<cstring>
  8. #define reg register
  9. #define EN std::puts("")
  10. #define LL long long
  11. inline int read(){
  12. register int x=0;register int y=1;
  13. register char c=std::getchar();
  14. while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
  15. while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
  16. return y?x:-x;
  17. }
  18. #define N 200006
  19. #define M 800006
  20. int n,m,vertex;
  21. inline int get_min(int x,int y){return x<y?x:y;}
  22. struct graph{
  23. int fir[N],nex[M],to[M],w[M],tot;
  24. inline void add(int u,int v,int len){
  25. to[++tot]=v;w[tot]=len;
  26. nex[tot]=fir[u];fir[u]=tot;
  27. }
  28. inline void clear(){
  29. std::memset(fir,0,sizeof fir);std::memset(nex,0,sizeof nex);
  30. tot=0;
  31. }
  32. }G;
  33. struct shortest_path{
  34. int dis[N],heap[N],size,in[N];
  35. inline void push(int x){
  36. heap[++size]=x;
  37. reg int i=size,fa;
  38. while(i>1){
  39. fa=i>>1;
  40. if(dis[heap[fa]]<=dis[heap[i]]) return;
  41. std::swap(heap[fa],heap[i]);i=fa;
  42. }
  43. }
  44. inline int pop(){
  45. int ret=heap[1];heap[1]=heap[size--];
  46. int i=1,ls,rs;
  47. while((i<<1)<=size){
  48. ls=i<<1;rs=ls|1;
  49. if(rs<=size&&dis[heap[rs]]<dis[heap[ls]]) ls=rs;
  50. if(dis[heap[ls]]>=dis[heap[i]]) break;
  51. std::swap(heap[i],heap[ls]);i=ls;
  52. }
  53. return ret;
  54. }
  55. inline void dij(int start){
  56. std::memset(dis,0x3f,sizeof dis);dis[start]=0;
  57. push(start);in[start]=1;
  58. while(size){
  59. reg int u=pop();in[u]=0;
  60. for(reg int v,i=G.fir[u];i;i=G.nex[i]){
  61. v=G.to[i];
  62. if(dis[v]>dis[u]+G.w[i]){
  63. dis[v]=dis[u]+G.w[i];
  64. if(!in[v]) push(v),in[v]=1;
  65. }
  66. }
  67. }
  68. }
  69. }path;
  70. struct kruskal_tree{
  71. struct edge{
  72. int u,v,w;
  73. }e[M];
  74. inline int find(int k){
  75. return k==up[k]?k:up[k]=find(up[k]);
  76. }
  77. static inline int cmp(edge aa,edge aaa){return aa.w>aaa.w;}
  78. int up[N*2],son[2][N*2],val[N*2];
  79. int fa[23][N*2],min_dis[N*2];
  80. inline void kruskal(){
  81. std::sort(e+1,e+1+m,cmp);
  82. vertex=n;
  83. for(reg int i=1;i<n*2;i++) up[i]=i;
  84. for(reg int u,v,i=1,cnt=1;cnt<n;i++){
  85. u=find(e[i].u);v=find(e[i].v);
  86. if(u==v) continue;
  87. val[++vertex]=e[i].w;
  88. son[0][vertex]=u;son[1][vertex]=v;
  89. cnt++;up[u]=up[v]=vertex;
  90. }
  91. }
  92. void dfs(int u){
  93. // std::printf("now : %d\n",u);
  94. for(reg int i=1;i<20;i++) fa[i][u]=fa[i-1][fa[i-1][u]];
  95. min_dis[u]=u<=n?path.dis[u]:0x3f3f3f3f;
  96. if(son[0][u]){
  97. fa[0][son[0][u]]=u;
  98. dfs(son[0][u]);
  99. min_dis[u]=get_min(min_dis[u],min_dis[son[0][u]]);
  100. }
  101. if(son[1][u]){
  102. fa[0][son[1][u]]=u;
  103. dfs(son[1][u]);
  104. min_dis[u]=get_min(min_dis[u],min_dis[son[1][u]]);
  105. }
  106. }
  107. inline int get_min_dis(reg int u,reg int p){
  108. for(reg int i=19;~i;i--)
  109. if(val[fa[i][u]]>p) u=fa[i][u];
  110. return min_dis[u];
  111. }
  112. }TREE;
  113. inline void clear(){
  114. std::memset(TREE.fa,0,sizeof TREE.fa);std::memset(TREE.min_dis,0,sizeof TREE.min_dis);
  115. std::memset(TREE.val,0,sizeof TREE.val);std::memset(TREE.son,0,sizeof TREE.son);
  116. G.clear();
  117. // std::puts("\n\n---------------------------------------------\n\n");
  118. }
  119. int main(){
  120. // std::freopen("return.in","r",stdin);
  121. // std::freopen("return.out","w",stdout);
  122. int CASES=read();while(CASES--){
  123. n=read();m=read();
  124. for(reg int w,i=1;i<=m;i++){
  125. TREE.e[i].u=read();TREE.e[i].v=read();w=read();TREE.e[i].w=read();
  126. G.add(TREE.e[i].u,TREE.e[i].v,w);G.add(TREE.e[i].v,TREE.e[i].u,w);
  127. }
  128. path.dij(1);
  129. TREE.kruskal();
  130. TREE.fa[0][vertex]=vertex;TREE.dfs(vertex);
  131. int q=read(),K=read(),S=read();
  132. reg int lastans=0,u,p;
  133. while(q--){
  134. u=(read()+(LL)K*lastans-1)%n+1;p=(read()+(LL)K*lastans)%(S+1);
  135. lastans=TREE.get_min_dis(u,p);
  136. std::printf("%d\n",lastans);
  137. }
  138. if(CASES) clear();
  139. }
  140. return 0;
  141. }

P4768 [NOI2018]归程(kruskal 重构树)的更多相关文章

  1. [洛谷P4768] [NOI2018]归程 (kruskal重构树模板讲解)

    洛谷题目链接:[NOI2018]归程 因为题面复制过来有点炸格式,所以要看题目就点一下链接吧\(qwq\) 题意: 在一张无向图上,每一条边都有一个长度和海拔高度,小\(Y\)的家在\(1\)节点,并 ...

  2. 洛谷P4768 [NOI2018]归程(Kruskal重构树)

    题意 直接看题目吧,不好描述 Sol 考虑暴力做法 首先预处理出从$1$到每个节点的最短路, 对于每次询问,暴力的从这个点BFS,从能走到的点里面取$min$ 考虑如何优化,这里要用到Kruskal重 ...

  3. [NOI2018]归程 kruskal重构树

    [NOI2018]归程 LG传送门 kruskal重构树模板题. 另一篇文章里有关于kruskal重构树更详细的介绍和更板子的题目. 题意懒得说了,这题的关键在于快速找出从查询的点出发能到达的点(即经 ...

  4. LOJ.2718.[NOI2018]归程(Kruskal重构树 倍增)

    LOJ2718 BZOJ5415 洛谷P4768 Rank3+Rank1无压力 BZOJ最初还不是一道权限题... Update 2019.1.5 UOJ上被hack了....好像是纯一条链的数据过不 ...

  5. BZOJ5415[Noi2018]归程——kruskal重构树+倍增+堆优化dijkstra

    题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 n 个节点.m 条边的无向连通图(节点的编号从 1 至 n).我们依次用 l,a 描述一条边的长度.海 ...

  6. BZOJ 5415: [Noi2018]归程(kruskal重构树)

    解题思路 \(NOI2018\)的\(Day1\) \(T1\),当时打网络赛的时候不会做.学了一下\(kruskal\)重构树后发现问题迎刃而解了.根据\(kruskal\)的性质,如果要找从\(u ...

  7. 洛谷$P4768\ [NOI2018]$归程 $kruscal$重构树

    正解:$kruscal$重构树 解题报告: 传送门$QwQ$ 语文不好选手没有人权$TT$连题目都看不懂真的要哭了$kk$ 所以先放个题目大意?就说给定一个$n$个点,$m$条边的图,每条边有长度和海 ...

  8. BZOJ_5415_[Noi2018]归程_kruscal重构树+倍增+最短路

    BZOJ_5415_[Noi2018]归程_kruscal重构树+倍增 Description www.lydsy.com/JudgeOnline/upload/noi2018day1.pdf 好久不 ...

  9. NOI Day1T1归程(Kruskal重构树+Dijkstra)

    NOI Day1T1归程(Kruskal重构树+Dijkstra) 题目 洛谷题目传送门 题解 其实我不想写......,所以...... 挖个坑......我以后一定会补的 luogu的题解讲的还是 ...

  10. #2718. 「NOI2018」归程 kruskal重构树

    链接 https://loj.ac/problem/2718 思路 我们希望x所在的连通块尽量的大,而且尽量走高处 离线的话可以询问排序,kruskal过程中更新答案 在线就要用kruskal重构树 ...

随机推荐

  1. Adb adb push (remote write failed: No space left on device)

    修改完成程序后, mm 后, 准备要push 进到公司测试手机里面的.之前还真的没有遇到过这个问题,查了一下, 应该是手机没空间了的 sudo adb root sudo adb remount su ...

  2. matplotlib Bbox类

    Bbox 类是一个可变的(相对于BboxBase)限位框, 继承自BboxBase 2020-04-07 22:54:57  --Edit by yangray 方法: __init__(points ...

  3. Python设计模式(11)-状态模式

    # coding=utf-8 # *状态模式:一个方法的判断逻辑太长,就不容易修改.方法过长,其本质就是,# * 就是本类在不同条件下的状态转移.状态模式,就是将这些判断分开到各个能# * 表示当前状 ...

  4. Python设计模式(1)-简单工厂模式

    为操作数据库设计增删改查操作 # coding=utf-8class DbManager: def __init__(self): pass def operate_db(self): pass cl ...

  5. Linux环境安装Docker

    1. 使用APT安装 # 更新数据源 apt-get update # 安装所需依赖 apt-get -y install apt-transport-https ca-certificates cu ...

  6. 不错的spring学习博客

    http://blog.csdn.net/xyh820/article/details/7303330/

  7. EXPLAIN 关键字可以 查看 sql执行 的详细过程

    EXPLAIN SELECT n_did,n_count,n_total,d_last_exchange FROM player_con_record WHERE n_roleid=1 AND n_f ...

  8. 代码质量管理 SonarQube 系列之 安装

    简介 SonarQube 是一个开源的代码质量管理系统. 功能介绍: 15种语言的静态代码分析 Java.JavaScript.C#.TypeScript.Kotlin.Ruby.Go.Scala.F ...

  9. 小白们错过就没了!Python基础之注释&变量命名

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:DZQTesters PS:如有需要Python学习资料的小伙伴可以加 ...

  10. I - Fill The Bag codeforces 1303D

    题解:注意这里的数组a中的元素,全部都是2的整数幂.然后有二进制可以拼成任意数.只要一堆2的整数幂的和大于x,x也是2的整数幂,那么那一堆2的整数幂一定可以组成x. 思路:位运算,对每一位,如果该位置 ...