题目链接: UOJ LOJ

感觉 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] 归程的更多相关文章

  1. [NOI2018]归程 kruskal重构树

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

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

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

  3. [luogu4768] [NOI2018] 归程 (Dijkstra+Kruskal重构树)

    [luogu4768] [NOI2018] 归程 (Dijkstra+Kruskal重构树) 题面 题面较长,这里就不贴了 分析 看到不能经过有积水的边,即不能经过边权小于一定值的边,我们想到了kru ...

  4. Luogu P4768 [NOI2018]归程(Dijkstra+Kruskal重构树)

    P4768 [NOI2018]归程 题面 题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 \(n\) 个节点. \(m\) 条边的无向连通图(节点的编 ...

  5. P4768 [NOI2018]归程(kruskal 重构树)

    洛谷P4768 [NOI2018]归程 LOJ#2718.「NOI2018」归程 用到 kruskal 重构树,所以先说这是个啥 显然,这和 kruskal 算法有关系 (废话 这个重构树是一个有点权 ...

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

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

  7. 2018.07.18 [NOI2018]归程(return)(kruskal重构树)

    传送门 新鲜出炉的noi2018试题. 下面讲讲这题的解法: 首先要学习一个叫做kruskal重构树的东东. 听名字就知道跟kruskal算法有关,没错,原来的kruskal算法就是用并查集实现的,但 ...

  8. NOI2018归程(Kruskal重构树)

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

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

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

随机推荐

  1. ixgbe 驱动 为xxx驱动做准备1

    网卡都是pci设备,因此这里每个网卡驱动其实就是一个pci驱动.并且intel这里是把好几个万兆网卡(82599/82598/x540)的驱动做在一起的.V4L2 一样几个类型摄像头合并在一起 先说一 ...

  2. wireguard使用

    1.编译与安装 sudo apt-get install libmnl-dev libelf-dev linux-headers-$(uname -r) build-essential pkg-con ...

  3. 支持jewel版本的calamari

    之前测试了下,发现calamari不支持jewel版本的,是因为接口了有了一些变化,在提出这个问题后,作者给出了回答,说肯定会支持的,并且做了一点小的改动,就可以支持了,这个作者merge了到了git ...

  4. Linux内核源码分析之set_arch (一)

    1. 概述 之前已经写了几篇Linux内核启动相关的文章,比如:<解压内核镜像><调用 start_kernel>都是用汇编语言写的,这些代码的作用仅仅是把内核镜像放置到特定的 ...

  5. 微信支付回调 敏感信息解密 v3 php

    今天博主用了一波微信的v3版本的支付,支付成功后发现回调跟v2的完全不一样,于是去看了了一波v3的文档,发现信息是经过加密的,需要解密才能获取的到 但是最悲催的是文档上没写怎么解密的,经过了一下午的百 ...

  6. hive显示列名

    查询时显示列名:hive> set hive.cli.print.header;hive.cli.print.header=falsehive> set hive.cli.print.he ...

  7. kali Linux的 安装详细步骤

    一.打开"kali 官方网站,下载kali镜像文件.地址(https://www.kali.org/downloads/)   在Download菜单界面下,有历史版本下载和最新版下载.找到 ...

  8. Mac电脑数据被误删了怎么办,还能恢复吗

    随着苹果产品的使用率越来越高,苹果电脑视频丢失的风险也是居高不下,大部分情况下都是由于误操作或者是中病毒导致视频丢失,苹果电脑视频恢复可以实现吗?涉及到文件恢复的问题,找EasyRecovery文件恢 ...

  9. DNS系列—dig命令的使用

    目录 如何安装dig dig常见用法 dig的基本语法 简单dig查询域名 指定DNS服务器查询 反查IP对应域名 如何安装dig dig是bind下面常见的工具,在linux系统上经常回用的一个dn ...

  10. Idea中如何导入jar包

    1.首先在idea左上角找到" File ",然后找到 "Project structure" 2.接着选择 " java ",选择后接着会 ...