传送门: https://www.luogu.com.cn/problem/P4768
题意
在
一
个
雨
天
漫
步
的
无
向
联
通
城
市
,
有
n
个
点
,
m
条
边
,
已
知
小
明
的
家
是
1
号
点
。
在一个雨天漫步的无向联通城市,有n个点,m条边,已知小明的家是1号点。
在一个雨天漫步的无向联通城市,有n个点,m条边,已知小明的家是1号点。
每
条
边
有
u
,
v
,
l
,
a
,
分
别
代
表
起
点
,
中
点
,
长
度
和
海
拔
。
每条边有u,v,l,a,分别代表起点,中点,长度和海拔。
每条边有u,v,l,a,分别代表起点,中点,长度和海拔。
给
Q
个
询
问
,
每
次
给
一
个
v
出
发
点
和
p
水
位
线
(
强
制
在
线
)
。
给Q个询问,每次给一个v出发点和p水位线(强制在线)。
给Q个询问,每次给一个v出发点和p水位线(强制在线)。
小
明
从
点
v
乘
车
出
发
,
当
一
条
边
的
海
拔
小
于
等
于
p
时
,
汽
车
可
以
通
过
,
否
则
必
须
弃
车
步
行
。
小明从点v乘车出发,当一条边的海拔小于等于p时,汽车可以通过,否则必须弃车步行。
小明从点v乘车出发,当一条边的海拔小于等于p时,汽车可以通过,否则必须弃车步行。
步
行
不
需
要
考
虑
水
位
线
。
\red{步行不需要考虑水位线。}
步行不需要考虑水位线。
请
输
出
小
明
在
一
次
询
问
中
步
行
的
最
小
步
行
距
离
。
请输出小明在一次询问中步行的最小步行距离。
请输出小明在一次询问中步行的最小步行距离。
思路
首 先 这 一 题 , 题 目 很 长 , 很 难 读 ! ! ! 首先这一题,题目很长,很难读!!! 首先这一题,题目很长,很难读!!!
题 意 转 化 一 下 , 小 明 先 乘 车 到 一 个 点 , 由 于 水 位 线 的 原 因 不 得 不 弃 车 , 即 先 乘 车 后 步 行 。 题意转化一下,小明先乘车到一个点,由于水位线的原因不得不弃车,即先乘车后步行。 题意转化一下,小明先乘车到一个点,由于水位线的原因不得不弃车,即先乘车后步行。
这 个 点 就 是 与 v 联 通 的 所 有 点 中 , 与 1 距 离 最 短 的 点 。 所 以 怎 么 找 到 这 个 关 键 点 呢 ? 这个点就是与v联通的所有点中,与1距离最短的点。所以怎么找到这个关键点呢? 这个点就是与v联通的所有点中,与1距离最短的点。所以怎么找到这个关键点呢?
这 就 要 请 出 今 天 的 主 角 : K r u s k a l 重 构 树 这就要请出今天的主角:\red{Kruskal重构树} 这就要请出今天的主角:Kruskal重构树
我 们 以 海 拔 为 边 权 , 建 立 一 棵 边 权 从 大 到 小 的 重 构 树 。 我们以海拔为边权,建立一棵边权从大到小的重构树。 我们以海拔为边权,建立一棵边权从大到小的重构树。
假
设
我
们
找
到
了
一
棵
子
树
的
根
节
点
u
,
且
v
a
l
[
u
]
>
p
&
&
v
a
l
[
f
a
[
u
]
]
<
=
p
.
假设我们找到了一棵子树的根节点u,且val[u]>p\;\;\&\&\;\;val[fa[u]]<=p.
假设我们找到了一棵子树的根节点u,且val[u]>p&&val[fa[u]]<=p.
那
么
从
u
出
发
到
子
树
的
任
意
节
点
不
需
要
步
行
,
所
以
要
在
很
多
叶
子
结
点
找
距
离
1
最
短
距
离
的
点
−
−
−
关
键
点
。
那么从u出发到子树的任意节点不需要步行,所以要在很多叶子结点找距离1最短距离的点---关键点。
那么从u出发到子树的任意节点不需要步行,所以要在很多叶子结点找距离1最短距离的点−−−关键点。
总 结 一 下 算 法 流 程 : 总结一下算法流程: 总结一下算法流程:
- 第 一 步 , 先 跑 最 短 路 , 找 到 1 到 任 意 点 的 最 短 距 离 , 方 便 第 三 步 更 新 。 第一步,先跑最短路,找到1到任意点的最短距离,方便第三步更新。 第一步,先跑最短路,找到1到任意点的最短距离,方便第三步更新。
- 第 二 步 , 建 重 构 树 , 把 问 题 转 化 成 树 形 结 构 。 第二步,建重构树,把问题转化成树形结构。 第二步,建重构树,把问题转化成树形结构。
- 第 三 步 , 处 理 关 键 点 , 可 树 形 d p , 可 树 上 倍 增 , 找 到 与 1 距 离 最 短 的 点 且 满 足 v a l [ u ] > p 。 第三步,处理关键点,可树形dp,可树上倍增,找到与1距离最短的点且满足val[u]>p。 第三步,处理关键点,可树形dp,可树上倍增,找到与1距离最短的点且满足val[u]>p。
Code
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
#define endl "\n"
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 10, M = 2e6 + 10;
int n, m;
/*----------最短路-------------*/
struct edge {
int v, next, w;
}e[M];
int head[M], cnt;
struct node {
int now, d;
bool operator < (const node& rhs) const {
return d > rhs.d;
}
};
int dis[N];
bool vis[N];
void init() {
for(int i = 1;i <= n; i++) head[i] = -1;
cnt = 0;
}
void add(int u, int v, int w) {
e[cnt] = {v, head[u], w};
head[u] = cnt++;
}
void Dijkstra(int s) {
priority_queue<node> q;
for(int i = 1;i <= n; i++) dis[i] = INF, vis[i] = 0;
dis[s] = 0;
q.push({s, 0});
while(!q.empty()) {
node p = q.top(); q.pop();
int u = p.now;
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if(dis[v] > dis[u] + e[i].w) {
dis[v] = dis[u] + e[i].w;
if(!vis[v]) q.push({v, dis[v]});
}
}
}
}
/*-------------Kruskal重构树---------------*/
struct Edge {
int u, v, w;
bool operator < (const Edge& rhs) const {
return w > rhs.w;
}
}E[M];
vector<int> g[N];
int f[N], val[N], tot;
int fa[N][33], d[N];
void dfs(int u, int par) {
d[u] = (u <= n ? dis[u] : INF);
fa[u][0] = par;
for(int i = 1;i <= 32; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(auto v : g[u]) {
if(v == par) continue;
dfs(v, u);
d[u] = min(d[u], d[v]);
}
g[u].clear();
}
int get(int u, int p) {
for(int i = 32; i >= 0; i--) {
if(val[fa[u][i]] > p) u = fa[u][i];
}
return u;
}
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
void EX_Kruskal() {
tot = n;
for(int i = 1;i < (n << 1); i++) f[i] = i, val[i] = 0;
sort(E + 1, E + m + 1);
for(int i = 1;i <= m; i++) {
int u = find(E[i].u);
int v = find(E[i].v);
if(u == v) continue;
val[++tot] = E[i].w;
f[u] = f[v] = tot;
g[u].emplace_back(tot); g[tot].emplace_back(u);
g[v].emplace_back(tot); g[tot].emplace_back(v);
if(tot == (n << 1) - 1) break;
}
int rt = find(1);
dfs(rt, 0);
}
void solve() {
int _; cin >> _;
while(_--) {
cin >> n >> m;
init();
for(int i = 1;i <= m; i++) {
int u, v, l, a; cin >> u >> v >> l >> a;
E[i] = {u, v, a};
add(u, v, l); add(v, u, l);
}
Dijkstra(1); // 先跑最短路
EX_Kruskal(); // 在建重构树
int lastans = 0;
int Q, K, S; cin >> Q >> K >> S;
while(Q--) {
int v, p; cin >> v >> p;
v = (v + K * lastans - 1) % n + 1;
p = (p + K * lastans) % (S + 1);
cout << (lastans = d[get(v, p)]) << endl;
}
}
}
signed main() {
solve();
}