P4768 [NOI2018]归程

题面

题目描述

本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定。 魔力之都可以抽象成一个 \(n\) 个节点、 \(m\) 条边的无向连通图(节点的编号从 \(1\) 至 \(n\) )。我们依次用 \(l,a\) 描述一条边的长度、海拔。 作为季风气候的代表城市,魔力之都时常有雨水相伴,因此道路积水总是不可避免 的。由于整个城市的排水系统连通,因此有积水的边一定是海拔相对最低的一些边。我们用水位线来描述降雨的程度,它的意义是:所有海拔不超过水位线的边都是有积水的。

\(Yazid\) 是一名来自魔力之都的OIer,刚参加完 \(ION2018\) 的他将踏上归程,回到他 温暖的家。 \(Yazid\) 的家恰好在魔力之都的 \(1\) 号节点。对于接下来 \(Q\) 天,每一天 \(Yazid\) 都会告诉你他的出发点 \(v\) ,以及当天的水位线 \(p\) 。 每一天, \(Yazid\) 在出发点都拥有一辆车。这辆车由于一些故障不能经过有积水的边。 \(Yazid\) 可以在任意节点下车,这样接下来他就可以步行经过有积水的边。但车会被留在他下车的节点并不会再被使用。 需要特殊说明的是,第二天车会被重置,这意味着:

  • 车会在新的出发点被准备好。
  • \(Yazid\) 不能利用之前在某处停放的车。 \(Yazid\) 非常讨厌在雨天步行,因此他希望在完成回家这一目标的同时,最小化他步行经过的边的总长度。请你帮助 \(Yazid\) 进行计算。 本题的部分测试点将强制在线,具体细节请见【输入格式】和【子任务】。

输入输出格式

输入格式:

单个测试点中包含多组数据。输入的第一行为一个非负整数 \(T\) ,表示数据的组数。

接下来依次描述每组数据,对于每组数据:

第一行 \(2\) 个非负整数 \(n,m\) ,分别表示节点数、边数。

接下来 \(m\) 行,每行 \(4\) 个正整数 \(u, v, l, a\) ,描述一条连接节点 \(u, v\) 的、长度为 \(l\) 、海拔为 \(a\) 的边。 在这里,我们保证 \(1 \leq u,v \leq n\) 。

接下来一行 \(3\) 个非负数 \(Q, K, S\) ,其中 \(Q\) 表示总天数, \(K \in {0,1}\) 是一个会在下面被用到的系数, \(S\) 表示的是可能的最高水位线。

接下来 \(Q\) 行依次描述每天的状况。每行 \(2\) 个整数 \(v_0; p_0\) 描述一天:

这一天的出发节点为 \(v = (v_0 + K \times \mathrm{lastans} - 1) \bmod n + 1\) 。

这一天的水位线为 \(p = (p_0 + K \times \mathrm{lastans}) \bmod (S + 1)\) 。

其中 \(lastans\) 表示上一天的答案(最小步行总路程)。特别地,我们规定第 \(1\) 天时 \(lastans = 0\) 。在这里,我们保证 \(1 \leq v_0 \leq n,0 \leq p_0 \leq S\) 。

对于输入中的每一行,如果该行包含多个数,则用单个空格将它们隔开。

输出格式:

依次输出各组数据的答案。对于每组数据:

  • 输出 \(Q\) 行每行一个整数,依次表示每天的最小步行总路程。

输入输出样例

输入样例:

1
4 3
1 2 50 1
2 3 100 2
3 4 50 1
5 0 2
3 0
2 1
4 1
3 1
3 2

输出样例:

0
50
200
50
150

输入样例:

1
5 5
1 2 1 2
2 3 1 2
4 3 1 2
5 3 1 2
1 5 2 1
4 1 3
5 1
5 2
2 0
4 0

输出样例:

0
2
3
1

说明

【样例1 解释】 第一天没有降水, \(Yazid\) 可以坐车直接回到家中。

第二天、第三天、第四天的积水情况相同,均为连接 \(1; 2\) 号节点的边、连接 \(3; 4\) 号 点的边有积水。

对于第二天, \(Yazid\) 从 \(2\) 号点出发坐车只能去往 \(3\) 号节点,对回家没有帮助。因此 \(Yazid\) 只能纯靠徒步回家。

对于第三天,从 \(4\) 号节点出发的唯一一条边是有积水的,车也就变得无用了。 \(Yazid\) 只能纯靠徒步回家。

对于第四天, \(Yazid\) 可以坐车先到达 \(2\) 号节点,再步行回家。

第五天所有的边都积水了,因此 \(Yazid\) 只能纯靠徒步回家。

本组数据强制在线。

本组数据强制在线。

第一天的答案是 \(0\) ,因此第二天的 \(v= \left ( 5+0-1 \right ) \bmod 5+1=5\) , \(p=\left(2+0\right)\bmod\left(3+1\right)=2\) 。

第二天的答案是 \(2\) ,因此第三天的 \(v= \left ( 2+2-1 \right ) \bmod 5+1=4\) , \(p=\left(0+2\right)\bmod\left(3+1\right)=2\) 。

第三天的答案是 \(3\) ,因此第四天的 \(v= \left ( 4+3-1 \right ) \bmod 5+1=2\) , \(p=\left(0+3\right)\bmod\left(3+1\right)=3\) 。

所有测试点均保证 \(T \leq 3\) ,所有测试点中的所有数据均满足如下限制:

  • \(n\leq 2\times 10^5\) , \(m\leq 4\times 10^5\) , \(Q\leq 4\times 10^5\) , \(K\in\left\{0,1\right\}K∈{0,1}\) , \(1\leq S\leq 10^9\) 。
  • 对于所有边: \(l \leq 10^4\) , \(a \leq 10^9\) 。
  • 任意两点之间都直接或间接通过边相连。

为了方便你快速理解,我们在表格中使用了一些简单易懂的表述。在此,我们对这些内容作形式化的说明:

  • 图形态:对于表格中该项为“一棵树”或“一条链”的测试点,保证 \(m = n-1\) 。 除此之外,这两类测试点分别满足如下限制:
  • 一棵树:保证输入的图是一棵树,即保证边不会构成回路。
  • 一条链:保证所有边满足 \(u + 1 = v\) 。
  • 海拔:对于表格中该项为“一种”的测试点,保证对于所有边有 \(a = 1\) 。
  • 强制在线:对于表格中该项为“是”的测试点,保证 \(K = 1\) ;如果该项为“否”, 则有 \(K = 0\) 。
  • 对于所有测试点,如果上述对应项为“不保证”,则对该项内容不作任何保证。
\(n\) \(m\) \(Q=\) 测试点 形态 海拔 强制在线
\(\leq 1\) \(\leq 0\) \(0\) \(1\) 不保证 一种
\(\leq 6\) \(\leq 10\) \(10\) \(2\) 不保证 一种
\(\leq 50\) \(\leq 150\) \(100\) \(3\) 不保证 一种
\(\leq 100\) \(\leq 300\) \(200\) \(4\) 不保证 一种
\(\leq 1500\) \(\leq 4000\) \(2000\) \(5\) 不保证 一种
\(\leq 200000\) \(\leq 400000\) \(100000\) \(6\) 不保证 一种
\(\leq 1500\) \(=n-1\) \(2000\) \(7\) 一条链 不保证
\(\leq 1500\) \(=n-1\) \(2000\) \(8\) 一条链 不保证
\(\leq 1500\) \(=n-1\) \(2000\) \(9\) 一条链 不保证
\(\leq 200000\) \(=n-1\) \(100000\) \(10\) 一棵树 不保证
\(\leq 200000\) \(=n-1\) \(100000\) \(11\) 一棵树 不保证
\(\leq 200000\) \(\leq 400000\) \(100000\) \(12\) 不保证 不保证
\(\leq 200000\) \(\leq 400000\) \(100000\) \(13\) 不保证 不保证
\(\leq 200000\) \(\leq 400000\) \(100000\) \(14\) 不保证 不保证
\(\leq 1500\) \(\leq 4000\) \(2000\) \(15\) 不保证 不保证
\(\leq 1500\) \(\leq 4000\) \(2000\) \(16\) 不保证 不保证
\(\leq 200000\) \(\leq 400000\) \(100000\) \(17\) 不保证 不保证
\(\leq 200000\) \(\leq 400000\) \(100000\) \(18\) 不保证 不保证
\(\leq 200000\) \(\leq 400000\) \(400000\) \(19\) 不保证 不保证
\(\leq 200000\) \(\leq 400000\) \(400000\) \(20\) 不保证 不保证

思路

完了,这道题卡 \(SPFA\) ! --czk

两个月前考场上做这道题,毫不犹豫 \(SPFA\) 然后走人,结果这道题居然要用 \(Dijkstra\) ......两个月后终于把它 \(A\) 掉了。

首先我们对所有的边按照其海拔进行排序,跑 \(Kruskal\) 重构树,那么就可以得到一棵海拔小顶堆。从 \(v\) 点出发,他的任意一个父亲节点若是满足其海拔 \(> p\) ,那么该父亲节点下的叶子节点对于 \(v\) 来说都是可以不通过步行可达的。

所以我们预处理这样的一些东西:

  • \(Kruskal\) 重构树,树上的非叶子结点储存边的海拔高度;
  • 建立反向边用 \(Dijkstra\) 求出每一个结点到 \(1\) 结点的步行最短路;
  • 更新 \(Kruskal\) 重构树上的结点,用一个数组储存其儿子节点到 \(1\) 结点的最短路。

那么对于每次询问,我们就可以从 \(v\) 结点倍增地往上跳,跳到最高的满足海拔要求的祖先结点,然后输出其子树下所有叶子结点到 \(1\) 结点的最短路的最小值。

预处理是 \(O(m \log m+(n+m) \log n+n \log n)\) 的,一次查询是 \(O(log n)\) 的,时间复杂度十分优秀,可以通过此题。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PLL;
const LL MAXN=200005;
const LL MAXM=800005;
LL T,n,m,tot,q,k,s,lastans,fa[MAXN<<1],mnl[MAXN<<1],dep[MAXN<<1],val[MAXN<<1],f[MAXN<<1][20];
LL cnt,top[MAXN<<1],to[MAXN<<1],nex[MAXN<<1];
LL __cnt,__top[MAXN],__to[MAXM],__len[MAXM],__nex[MAXM];
bool vis[MAXN];
struct Edge
{
LL u,v,l,a;
bool operator < (const Edge& sjf) const {return a>sjf.a;}
}edge[MAXM];
inline void add_edge(LL x,LL y){to[++cnt]=y,nex[cnt]=top[x],top[x]=cnt;}
inline void __add_edge(LL x,LL y,LL z)
{
__to[++__cnt]=y,__len[__cnt]=z,__nex[__cnt]=__top[x],__top[x]=__cnt;
__to[++__cnt]=x,__len[__cnt]=z,__nex[__cnt]=__top[y],__top[y]=__cnt;
}
inline LL fd(LL x)
{
LL r=x;
while(r!=fa[r]) r=fa[r];
LL i=x,j;
while(i!=r) j=fa[i],fa[i]=r,i=j;
return r;
}
inline LL read()
{
LL re=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
void Dijkstra()
{
memset(vis,false,sizeof vis);
mnl[1]=0;
priority_queue<PLL,vector<PLL>,greater<PLL> >Q;
Q.push(make_pair(0,1));
while(!Q.empty())
{
LL now=Q.top().second;Q.pop();
if(vis[now]) continue;
vis[now]=true;
for(LL i=__top[now];i;i=__nex[i])
if(!vis[__to[i]]&&mnl[__to[i]]>mnl[now]+__len[i])
{
mnl[__to[i]]=mnl[now]+__len[i];
Q.push(make_pair(mnl[__to[i]],__to[i]));
}
}
}
void Kruskal()
{
LL now=1;
for(LL i=1;i<=n;i++) fa[i]=i;
sort(edge,edge+m);
for(LL i=0;i<m;i++)
{
LL fx=fd(edge[i].u),fy=fd(edge[i].v);
if(fx!=fy)
{
val[++tot]=edge[i].a;
fa[fx]=fa[fy]=fa[tot]=tot;
add_edge(tot,fx),add_edge(tot,fy);
now++;
}
if(now==n) return ;
}
}
void dfs(LL now,LL li_tong)
{
dep[now]=dep[li_tong]+1,f[now][0]=li_tong;
for(LL i=1;i<=19;i++) f[now][i]=f[f[now][i-1]][i-1];
for(LL i=top[now];i;i=nex[i])
{
dfs(to[i],now);
mnl[now]=min(mnl[now],mnl[to[i]]);
}
}
inline LL ask(LL x,LL y)
{
for(LL i=19;i>=0;i--) if(f[x][i]&&val[f[x][i]]>y) x=f[x][i];
return mnl[x];
}
int main()
{
T=read();
while(T--)
{
tot=n=read(),m=read(),cnt=__cnt=lastans=0;
memset(__top,0,sizeof __top);
memset(top,0,sizeof top);
memset(mnl,0x3f,sizeof mnl);
for(LL i=0;i<m;i++) edge[i].u=read(),edge[i].v=read(),edge[i].l=read(),edge[i].a=read(),__add_edge(edge[i].u,edge[i].v,edge[i].l);
Dijkstra();
Kruskal();
dfs(tot,0);
q=read(),k=read(),s=read();
while(q--)
{
LL x=(k*lastans+read()-1)%n+1,y=(k*lastans+read())%(s+1);
printf("%lld\n",lastans=ask(x,y));
}
}
return 0;
}

Luogu P4768 [NOI2018]归程(Dijkstra+Kruskal重构树)的更多相关文章

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

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

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

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

  3. NOI2018归程(Kruskal重构树)

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

  4. NOI2018d1t1 归程 (dijkstra+kruskal重构树)

    题意:给一张无向联通图,每条边有长度和高度,每次询问在高度大于p的边,从v点能到达的所有点到1号点的最短距离(强制在线) 首先dijkstra求出每个点到1号点的距离 易知:如果我按高度从高到低给边排 ...

  5. LOJ #2718. 「NOI2018」归程(Dijkstra + Kruskal重构树 + 倍增)

    题意 给你一个无向图,其中每条边有两个值 \(l, a\) 代表一条边的长度和海拔. 其中有 \(q\) 次询问(强制在线),每次询问给你两个参数 \(v, p\) ,表示在 \(v\) 出发,能开车 ...

  6. NOI2018 D1T1 洛谷P4768 归程 (Kruskal重构树)

    实际上是一个最短路问题,但加上了海拔这个条件限制,要在海拔<水位线p中找最短路. 这里使用Kruskal重构树,将其按海拔建成小根堆,我们就可以在树中用倍增找出他不得不下车的点:树中节点有两个权 ...

  7. Luogu P4768 [NOI2018]归程

    题目链接 \(Click\) \(Here\) \(Kruskal\)重构树的好题.想到的话就很好写,想不到乱搞的难度反而相当高. 按照点的水位,建出来满足小根队性质的\(Kruskal\)重构树,这 ...

  8. 「NOI 2018」归程「Kruskal 重构树」

    题解 Kruskal重构树:每次一条边连接两个集合,建一个新点,点权为该边边权:把这两个集合的根连向新点. 性质:(如果求的是最大生成树)叶子结点是图中实际结点:叶子到根路径上点权递减:两点间lca的 ...

  9. 【BZOJ5415&UOJ393】归程(Kruskal重构树,最短路)

    题意:From https://www.cnblogs.com/Memory-of-winter/p/11628351.html 思路:先从1开始跑一遍dijkstra,建出kruskal重构树之后每 ...

随机推荐

  1. MyBatis的核心API

    MyBatis核心Api 上次简单的写了一个MyBatis的简介以及编写了一个MyBatis的入门程序,但是在入门程序中出现多很多比较陌生的词,比如SqlSessionFactoryBuilder.S ...

  2. 总结windows cmd 查看进程,端口,硬盘信息

    1.查看window所有进程 tasklist 2.查看windows所占用的进程号 tasklist|findstr 1916 3.杀死进程,进程pid taskkill /f /pid 10156 ...

  3. java_JDK8中新增的时间API

    java.time 包含值对象的基础包 java.time.chrono 提供对不同的日历系统的访问 java.time.format 格式化和解析时间的日期 java.time.temporal 包 ...

  4. 【JZOJ3347】树的难题

    description analysis 比较麻烦树形\(DP\) 不过这个我还是不算很懂-- 下次要注意思考,不要怕麻烦 code #pragma GCC optimize("O3&quo ...

  5. strict

    strict为3.2.3新增连贯操作,用于设置数据写入和查询是否严格检查是否存在字段.默认情况下不合法数据字段自动删除,如果设置了严格检查则会抛出异常. 例如: $model->strict(t ...

  6. C++ Builder获取系统文件的路径

    取得路径的程序:(注意红色字体,由于博客显示问题,所以中间加了空格,大家自己把空格去掉即可) // -------------------------------------------------- ...

  7. js 数据绑定

    //   回流:(重排 reflow) 当HTML的DOM结构(删除.增加.位置等)发生改变时引起DOM回流.浏览器重新计算DOM结构,重新的对当前DOM结构进行渲染 //   重绘:某一个元素的部分 ...

  8. JS高级特性

    一.JavaScript的同源策略 参考链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Same_origin_policy_fo ...

  9. php析构函数小结

    l 基本语法 class  类名{ public  function  __destruct(){ //函数体 //析构函数的最重要的作用,就是释放对象创建的资源 //比如 数据库连接, 文件句柄, ...

  10. activiti 连线

    实际使用中工作流往往不是一条直线进行下去,例如请假的话可以部门经理直接同意,或者报总经理同意,流程图示意如下. 可以看到,面对一个事情,重要的才要总经理审批否则仅部门经理审批即可.因此在完成任务的时候 ...