Kruskal重构树——[NOI2018] 归程
感觉 Kruskal 重构树比较简单,就不单独开学习笔记了。
Statement
给定一个 \(n\) 点 \(m\) 边的无向连通图,用 \(l,a\) 描述一条边的长度、海拔。用水位线描述降雨,所有海拔不超过水位线的边都是有积水的。
有 \(q\) 次询问,每次给出出发点 \(v\) 和水位线 \(p\) ,开始会有一辆车,可以经过任意没有积水的边,可以在任意节点下车步行(车不会延续到下一次)。问回到 \(1\) 号点最小的步行长度。强制在线。
\(n\leq 2e5,m\leq 4e5,q\leq 4e5,1\leq S\leq 1e9,l\leq 1e4,a\leq 1e9\) .
Solution
名字挺高级的,事实上很简单。
重点解决的主要问题: 查询从某个点出发经过边权不超过 val 的边所能到达的节点 。
大致就是类似 点分治=>点分树 的思路,点分树是把点分治中所有分治重心按照层次关系连接起来形成树状结构,那么 Kruskal 重构树就是在 Kruskal 建最小生成树的时候,不直接合并两个点,而是新建一个虚点进行合并。好吧,看上去一点都不像
具体一点就看 这个博客 .
性质:一个点的所有子树节点的权值都不大于它的权值,并且从它开始逐渐向子节点移动,权值是单调不增的。
Proof
显然,考虑 Kruskal 的加边过程即可。
查询的时候进行树上倍增,得到能够到达的最远祖先,那么根据限制,能到达的连通块中的节点就是这个祖先点的子树中所有的叶节点。(除了叶节点都是虚点嘛)
回到这道题目。题目要求就是要将一条 \(v\to 1\) 的路径分成两部分,设断点为 \(u\) ,要满足 \(u\to v\) 的路径上所有边的海拔大于 \(p\) ,在此前提下 \(1\to u\) 最短。
那么很容易发现,合法 \(u\) 构成的点集在原图的最大生成树上。
根据上面的铺垫,容易想到使用 Kruskal 重构树:
把每条边按照海拔降序,求出重构树。按照上面的步骤,对于每个询问,树上倍增得到包含起点 \(v\) 的子树中根节点深度最小且海拔大于 \(p\) 的子树 \(x\) ,那么合法 \(u\) 的集合就是 \(x\) 子树内的所有叶子节点。现在就要在这些点中找出到点 \(1\) 的最短路。这个同样可以在 Kruskal 中求解出路径,并在每个节点统计子树内的最短路径长度即可,复杂度是 \(\mathcal{O}(1)\) 的。
总复杂度为 \(\mathcal{O}(T\times n\log n)\) .
Code
过不去 UOJ Extra Test 的多半是没开 long long.
//Author: RingweEH
const int N=2e5+10;
struct edge
{
int to,nxt,val;
}e[N<<2];
struct node
{
int u; ll dis;
bool operator < ( const node &tmp ) const { return dis>tmp.dis; }
};
struct edge2
{
int fro,to,val;
bool operator < ( const edge2 &tmp ) const { return val>tmp.val; }
}e2[N<<1];
int n,m,q,k,s,tot=0,head[N],fath[N<<1],fa[N<<1][21],a[N<<1],siz;
ll dis[N<<1],lasans;
void add( int u,int v,int w )
{
e[++tot].to=v; e[tot].nxt=head[u]; head[u]=tot; e[tot].val=w;
}
void Dijkstra()
{
priority_queue<node> q;
for ( int i=1; i<=n*2-1; i++ )
dis[i]=1e15;
dis[1]=0; q.push( (node){1,0} );
while ( !q.empty() )
{
node u=q.top(); q.pop();
if ( u.dis>dis[u.u] ) continue;
for ( int i=head[u.u]; i; i=e[i].nxt )
if ( u.dis+e[i].val<dis[e[i].to] ) q.push( (node){e[i].to,dis[e[i].to]=u.dis+e[i].val} );
}
}
int find( int x )
{
return x==fath[x] ? x : fath[x]=find(fath[x]);
}
void Kruskal()
{
memset( fa,0,sizeof(fa) ); siz=n;
for ( int i=1; i<n*2; i++ )
fath[i]=i;
sort( e2+1,e2+1+m );
for ( int i=1; i<=m; i++ )
{
int u=find( e2[i].fro ),v=find( e2[i].to );
if ( u==v ) continue;
fa[u][0]=fath[u]=fa[v][0]=fath[v]=++siz;
a[siz]=e2[i].val; dis[siz]=min( dis[u],dis[v] );
if ( siz==n*2-1 ) break;
}
for ( int i=siz; i>=1; i-- )
for ( int j=1; j<=18; j++ )
fa[i][j]=fa[fa[i][j-1]][j-1];
}
int main()
{
freopen( "return.in","r",stdin ); freopen( "return.out","w",stdout );
int T=read();
while ( T-- )
{
memset( head,0,sizeof(head) ); tot=lasans=0;
n=read(); m=read();
for ( int i=1; i<=m; i++ )
{
e2[i].fro=read(),e2[i].to=read(); int w=read(); e2[i].val=read();
add( e2[i].fro,e2[i].to,w ); add( e2[i].to,e2[i].fro,w );
}
q=read(); k=read(); s=read(); Dijkstra(); Kruskal();
while ( q-- )
{
int v=read(),p=read();
v=(v+k*lasans-1)%n+1; p=(p+k*lasans)%(s+1);
for ( int i=17; i>=0; i-- )
if ( fa[v][i] && a[fa[v][i]]>p ) v=fa[v][i];
printf( "%lld\n",dis[v] ); lasans=dis[v];
}
}
fclose( stdin ); fclose( stdout );
return 0;
}
Kruskal重构树——[NOI2018] 归程的更多相关文章
- [NOI2018]归程 kruskal重构树
[NOI2018]归程 LG传送门 kruskal重构树模板题. 另一篇文章里有关于kruskal重构树更详细的介绍和更板子的题目. 题意懒得说了,这题的关键在于快速找出从查询的点出发能到达的点(即经 ...
- [洛谷P4768] [NOI2018]归程 (kruskal重构树模板讲解)
洛谷题目链接:[NOI2018]归程 因为题面复制过来有点炸格式,所以要看题目就点一下链接吧\(qwq\) 题意: 在一张无向图上,每一条边都有一个长度和海拔高度,小\(Y\)的家在\(1\)节点,并 ...
- [luogu4768] [NOI2018] 归程 (Dijkstra+Kruskal重构树)
[luogu4768] [NOI2018] 归程 (Dijkstra+Kruskal重构树) 题面 题面较长,这里就不贴了 分析 看到不能经过有积水的边,即不能经过边权小于一定值的边,我们想到了kru ...
- Luogu P4768 [NOI2018]归程(Dijkstra+Kruskal重构树)
P4768 [NOI2018]归程 题面 题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 \(n\) 个节点. \(m\) 条边的无向连通图(节点的编 ...
- P4768 [NOI2018]归程(kruskal 重构树)
洛谷P4768 [NOI2018]归程 LOJ#2718.「NOI2018」归程 用到 kruskal 重构树,所以先说这是个啥 显然,这和 kruskal 算法有关系 (废话 这个重构树是一个有点权 ...
- BZOJ5415[Noi2018]归程——kruskal重构树+倍增+堆优化dijkstra
题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 n 个节点.m 条边的无向连通图(节点的编号从 1 至 n).我们依次用 l,a 描述一条边的长度.海 ...
- 2018.07.18 [NOI2018]归程(return)(kruskal重构树)
传送门 新鲜出炉的noi2018试题. 下面讲讲这题的解法: 首先要学习一个叫做kruskal重构树的东东. 听名字就知道跟kruskal算法有关,没错,原来的kruskal算法就是用并查集实现的,但 ...
- NOI2018归程(Kruskal重构树)
题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 n 个节点.m 条边的无向连通图(节点的编号从 1 至 n). 我们依次用 l,a 描述一条边的长度. ...
- BZOJ 5415: [Noi2018]归程(kruskal重构树)
解题思路 \(NOI2018\)的\(Day1\) \(T1\),当时打网络赛的时候不会做.学了一下\(kruskal\)重构树后发现问题迎刃而解了.根据\(kruskal\)的性质,如果要找从\(u ...
随机推荐
- 【JVM第六篇--对象】对象的实例化、内存布局和访问定位
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.对象的实例化 在平常写代码的过程中,我们用class关键字定义的类只是一个类的模 ...
- ASCII、Unicode、UTF-8、UTF-8(without BOM)、UTF-16、UTF-32傻傻分不清
ASCII.Unicode.UTF-8.UTF-8(without BOM).UTF-16.UTF-32傻傻分不清 目录 ASCII.Unicode.UTF-8.UTF-8(without BOM). ...
- PyQt5播放实时视频流或本地视频文件
目录 编辑UI 新建视频播放类Display 打开相机 关闭相机 显示视频画面 完整源代码 效果图 编辑UI 编辑UI如下图所示: 新建视频播放类Display 定义如下初始化函数 def __ini ...
- Spring源码之循环依赖
https://www.cnblogs.com/longy2012/articles/12834762.html https://www.bilibili.com/video/BV1iD4y1o7pM ...
- 使用Actor模型管理Web Worker多线程
前端固有的编程思维是单线程,比如JavaScript语言的单线程.浏览器JS线程与UI线程互斥等等,Web Woker是HTML5新增的能力,为前端带来多线程能力.这篇文章简单记录一下搜狗地图WebG ...
- IDEA “Cannot resolve symbol” 解决办法
IDEA 无法识别同一个 package 里的其他类,将其显示为红色,但是 compile 没有问题.鼠标放上去后显示 "Cannot resolve symbol XXX",重启 ...
- JS逆向课程笔记
扩展知识 Sources-js代码格式化
- MapReduce的工作流程
MapReduce的工作流程 1.客户端将每个block块切片(逻辑切分),每个切片都对应一个map任务,默认一个block块对应一个切片和一个map任务,split包含的信息:分片的元数据信息,包含 ...
- 掌握这些springboot的配置方式,让你工作效率翻个倍!
springboot的多种配置方式 java配置主要靠java类和一些注解,比较常用的注解有: @Configuration :声明一个类作为配置类,代替xml文件 @Bean :声明在方法上,将方法 ...
- 深度分析:Java中如何如理异常,一篇帮你搞定!
异常的背景 初识异常 我们曾经的代码中已经接触了一些 "异常" 了. 例如: 除以 0 System.out.println(10 / 0); // 执行结果 Exception ...