题目链接: 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. 消失的两个数字(1-N缺两个数)

    给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字.你能在 O(N) 时间内只用 O(1) 的空间找到它们吗? 以任意顺序返回这两个数字均可. 示例 1: 输入: [1]输出: [2,3 ...

  2. Python_编码错误解决办法 python3 UnicodeEncodeError: 'gbk' codec can't encode character '\xXX' in position XX

    先说解决办法:头部加几行代码 import io import sys sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='gb1803 ...

  3. Mysql_笔记2018.1.29

    1.主要数据库 Oracle MySQL Sqlsever 微软 MongoDB (非关系型数据库) 2.MySql 专业词语 1.数据库:一些关联表的集合 2.数据表:表示数据的矩阵 3.列:同ex ...

  4. 深入浅出!springboot从入门到精通,实战开发全套教程!

    前言 之前一直有粉丝想让我出一套springboot实战开发的教程,我这边总结了很久资料和经验,在最近总算把这套教程的大纲和内容初步总结完毕了,这份教程从springboot的入门到精通全部涵盖在内, ...

  5. 如何使用iMindMap制作更专业的时间计划

    时间计划无论是在日常生活中,还是在工作中,都显得极为重要.小到每周的购物时间规划,大到大型项目的时间管理,时间计划都会如影随形.虽然时间计划很重要,但很多人都会忽视这种重要性,可能只会在台本日历上作一 ...

  6. pdfFactory全景手柄使用方法介绍

    当文档中存在一些照片,或使用的字体过小时,大家可能会使用放大的功能,将文档的页面进行放大处理.此时,页面就会仅显示局部,为了查看页面的其他内容,就要使用到全景手柄来移动页面. pdfFactory的全 ...

  7. yii2.0 中数据查询中 or、in、between 及session的使用

    1 HTML: 2 3 <div> 4 <form class="form-inline " method="get" action=&quo ...

  8. DIV滚动条设置添加 CSS滚动条显示与滚动条隐藏

    <!DOCTYPE html> <html> <head> <meta charset="gb2312" /> <title& ...

  9. docker提示容器已存在

    docker ps -a docker rm 容器id 重启启动

  10. D - Number of Multisets 题解(思维dp)

    题目链接 题目大意 给你一个数k和n,表示用n个\(1/2^i(i=0,1,2.....)\)组成k有多少种方案数 题目思路 这个dp实属巧妙 设\(dp[i][j]表示i个数构成j\) 这i个数可以 ...