P4768 归程 最短路+Kruskal重构树

P4768 归程 最短路+Kruskal重构树


传送门: https://www.luogu.com.cn/problem/P4768

题意

在 一 个 雨 天 漫 步 的 无 向 联 通 城 市 , 有 n 个 点 , m 条 边 , 已 知 小 明 的 家 是 1 号 点 。 在一个雨天漫步的无向联通城市,有n个点,m条边,已知小明的家是1号点。 nm1
每 条 边 有 u , v , l , a , 分 别 代 表 起 点 , 中 点 , 长 度 和 海 拔 。 每条边有u,v,l,a,分别代表起点,中点,长度和海拔。 u,v,l,a

给 Q 个 询 问 , 每 次 给 一 个 v 出 发 点 和 p 水 位 线 ( 强 制 在 线 ) 。 给Q个询问,每次给一个v出发点和p水位线(强制在线)。 Qvp线(线)
小 明 从 点 v 乘 车 出 发 , 当 一 条 边 的 海 拔 小 于 等 于 p 时 , 汽 车 可 以 通 过 , 否 则 必 须 弃 车 步 行 。 小明从点v乘车出发,当一条边的海拔小于等于p时,汽车可以通过,否则必须弃车步行。 vp
步 行 不 需 要 考 虑 水 位 线 。 \red{步行不需要考虑水位线。} 线
请 输 出 小 明 在 一 次 询 问 中 步 行 的 最 小 步 行 距 离 。 请输出小明在一次询问中步行的最小步行距离。

思路

首 先 这 一 题 , 题 目 很 长 , 很 难 读 ! ! ! 首先这一题,题目很长,很难读!!!

题 意 转 化 一 下 , 小 明 先 乘 车 到 一 个 点 , 由 于 水 位 线 的 原 因 不 得 不 弃 车 , 即 先 乘 车 后 步 行 。 题意转化一下,小明先乘车到一个点,由于水位线的原因不得不弃车,即先乘车后步行。 线

这 个 点 就 是 与 v 联 通 的 所 有 点 中 , 与 1 距 离 最 短 的 点 。 所 以 怎 么 找 到 这 个 关 键 点 呢 ? 这个点就是与v联通的所有点中,与1距离最短的点。所以怎么找到这个关键点呢? v1

这 就 要 请 出 今 天 的 主 角 : 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. uval[u]>p&&val[fa[u]]<=p.
那 么 从 u 出 发 到 子 树 的 任 意 节 点 不 需 要 步 行 , 所 以 要 在 很 多 叶 子 结 点 找 距 离 1 最 短 距 离 的 点 − − − 关 键 点 。 那么从u出发到子树的任意节点不需要步行,所以要在很多叶子结点找距离1最短距离的点---关键点。 u1

总 结 一 下 算 法 流 程 : 总结一下算法流程:

  • 第 一 步 , 先 跑 最 短 路 , 找 到 1 到 任 意 点 的 最 短 距 离 , 方 便 第 三 步 更 新 。 第一步,先跑最短路,找到1到任意点的最短距离,方便第三步更新。 1便
  • 第 二 步 , 建 重 构 树 , 把 问 题 转 化 成 树 形 结 构 。 第二步,建重构树,把问题转化成树形结构。
  • 第 三 步 , 处 理 关 键 点 , 可 树 形 d p , 可 树 上 倍 增 , 找 到 与 1 距 离 最 短 的 点 且 满 足 v a l [ u ] > p 。 第三步,处理关键点,可树形dp,可树上倍增,找到与1距离最短的点且满足val[u]>p。 dp1val[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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值