P4768 [NOI2018] 归程

2022/7/24 23:25:57

本文主要是介绍P4768 [NOI2018] 归程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

题目大意

\(n(n\le2\cdot10^5)\) 个点, \(m(m\le4\cdot10^5)\) 条边的无向图,每条边有长度 \(l(l\le10^4)\) ,海拔 \(a(a\le10^9)\) , \(q(q\le 4\cdot10^5)\) 次询问,每次从节点 \(v\) 出发,可以乘车经过任意连续一段海拔 \(> p\) 的边,之后便只能步行,求到达节点 \(1\) 所需的最短步行里程,强制在线。

思路

一开始乘车能够走到的节点集合即为按海拔降序建立 \(\texttt{Kruscal}\) 重构树后,以深度最小的权值 \(>p\) 的节点为根的子树。答案就是这些节点到 \(1\) 的最短路的最小值,于是我们可以先预处理出每个节点到 \(1\) 的最短路,然后在重构树上算出子树中最短路的最小值,之后对于每次询问,在重构树上倍增寻找深度最小的权值 \(>p\) 的节点即可 。

代码

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<LL, LL>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 1004535809;
const int maxn = 600010;
const int maxm = 400010;

int TT, N, M, Q, K, S;
struct edge {
    int to, l, a;
};
struct Edge {
    int u, v, cost;
}es[maxm];
vector<edge>G[maxm];
vector<int>T[maxn];

void add_edge(int from, int to, int l, int a)
{
    G[from].push_back(edge{ to,l,a });
    G[to].push_back(edge{ from,l,a });
}

void add_edge(int from, int to)
{
    T[from].push_back(to);
    T[to].push_back(from);
}

bool cmp1(const Edge& x, const Edge& y)
{
    return x.cost > y.cost;
}

int par[maxn];

void init(int n)
{
    for (int i = 1; i <= n; i++)
        par[i] = i;
}

int find(int x)
{
    if (x == par[x])
        return x;
    return par[x] = find(par[x]);
}

void unite(int x, int y)
{
    x = find(x), y = find(y);
    if (x == y)
        return;
    par[y] = x;
}

bool same(int x, int y)
{
    return find(x) == find(y);
}

int val[maxn], cntv, cnte;

void Kruskal()
{
    sort(es + 1, es + M + 1, cmp1), init(cntv);
    for (int i = 1; i <= M; i++)
    {
        Edge& e = es[i];
        int u = e.u, v = e.v;
        if (!same(u, v))
        {
            int fu = find(u), fv = find(v);
            val[++cntv] = e.cost, par[cntv] = cntv;
            add_edge(fu, cntv), add_edge(cntv, fv);
            unite(cntv, fu), unite(cntv, fv);
        }
        if (cntv == N * 2 - 1)
            break;
    }
}

int d[maxn], fa[21][maxn], f[maxn];

void dijkstra()
{
    for (int i = 1; i <= N; i++)
        d[i] = INF;
    priority_queue<PII, vector<PII>, greater<PII>>que;
    d[1] = 0, que.push(PII(0, 1));
    while (!que.empty())
    {
        PII p = que.top(); que.pop();
        int v = p.second, dis = p.first;
        if (dis > d[v])
            continue;
        for (auto& e : G[v])
        {
            if (d[v] + e.l < d[e.to])
            {
                d[e.to] = d[v] + e.l;
                que.push(PII(d[e.to], e.to));
            }
        }
    }
}

void dfs(int v, int p)
{
    fa[0][v] = p;
    if (v <= N)
        f[v] = d[v];
    else
        f[v] = INF;
    for (auto& to : T[v])
    {
        if (to == p)
            continue;
        dfs(to, v);
        f[v] = min(f[to], f[v]);
    }
}

void init()
{
    dfs(cntv, 0);
    for (int k = 0; k + 1 < 20; k++)
    {
        for (int v = 1; v <= cntv; v++)
        {
            if (fa[k][v] <= 0)
                fa[k + 1][v] = 0;
            else
                fa[k + 1][v] = fa[k][fa[k][v]];
        }
    }
}

void solve()
{
    dijkstra(), Kruskal(), init();
    int ans = 0;
    cin >> Q >> K >> S;
    int v, p;
    while (Q--)
    {
        cin >> v >> p;
        v = (v + K * ans - 1) % N + 1;
        p = (p + K * ans) % (S + 1);
        while (true)
        {
            int lst = v;
            for (int j = 20; j >= 0; j--)
            {
                if (fa[j][v] <= 0)
                    continue;
                if (val[fa[j][v]] > p)
                    v = fa[j][v];
            }
            ans = f[v];
            if (v == lst)
                break;
        }
        cout << ans << endl;
    }
}

signed main()
{
	IOS;
    cin >> TT;
    while (TT--)
    {
        cin >> N >> M;
        for (int i = 1; i <= N + M; i++)
            T[i].clear();
        for (int i = 1; i <= N; i++)
            G[i].clear();
        cntv = N, cnte = M;
        int u, v, l, a;
        for (int i = 1; i <= M; i++)
        {
            cin >> u >> v >> l >> a;
            add_edge(u, v, l, a), es[i] = Edge{ u,v,a };
        }
        solve();
    }

	return 0;
}


这篇关于P4768 [NOI2018] 归程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程