1. 程式人生 > >Luogu P4768 [NOI2018]歸程

Luogu P4768 [NOI2018]歸程

ont void tps init 最短 初始 ref min prior

題目鏈接 \(Click\) \(Here\)

\(Kruskal\)重構樹的好題。想到的話就很好寫,想不到亂搞的難度反而相當高。

按照點的水位,建出來滿足小根隊性質的\(Kruskal\)重構樹,這樣一個點的子樹裏的點就是所有可以開車到達的點。做一遍最短路預處理,然後樹上求一個子樹\(min\),就可以得到子樹裏面的點到點\(1\)的最短距離。註意需要初始化。

#include <bits/stdc++.h>
using namespace std;

const int N = 800010;
const int INF = 0x7fffffff;

struct _edge {int u, v, l, a;}_e[N];

bool cmp (_edge lhs, _edge rhs) {
    return lhs.a > rhs.a;
}

struct Graph {
    int cnt, head[N];

    struct edge {
        int nxt, to, w;
    }e[N << 1];
    
    void Init () {
        cnt = 0;
        memset (head, 0, sizeof (head));
    }

    void add_edge (int u, int v, int w) {
        e[++cnt] = (edge) {head[u], v, w}; head[u] = cnt;
    }
}G, krus;

int read () {
    int s = 0, w = 1, ch = getchar ();
    while ('9' < ch || ch < '0') {
        if (ch == '-') w = -1;
        ch = getchar ();
    }
    while ('0' <= ch && ch <= '9') {
        s = s * 10 + ch - '0';
        ch = getchar ();
    }
    return s * w;
}

int T, n, m, Q, K, S, tot, fa[N], _high[N];

int find (int x) {
    return fa[x] == x ? x : fa[x] = find (fa[x]);
}

int deep[N], fafa[N][20];

int dis[N], mindis[N];

void dfs (int u, int fa) {
    fafa[u][0] = fa;
    mindis[u] = krus.head[u] == 0 ? dis[u] : INF;
    deep[u] = deep[fa] + 1;
    for (int i = 1; (1 << i) <= deep[u]; ++i) {
        fafa[u][i] = fafa[fafa[u][i - 1]][i - 1];
    }
    for (int i = krus.head[u]; i; i = krus.e[i].nxt) {
        int v = krus.e[i].to;
        dfs (v, u);
        mindis[u] = min (mindis[u], mindis[v]);
    }
}

void kruskal () {
    sort (_e + 1, _e + 1 + m, cmp);
    tot = n;
    for (int i = 1; i <= n; ++i) fa[i] = i;
    for (int i = 1; i <= m; ++i) {
        int u = find (_e[i].u);
        int v = find (_e[i].v);
        if (u != v) {
            int T = ++tot;
            _high[T] = _e[i].a;
            krus.add_edge (T, u, 0);
            krus.add_edge (T, v, 0);
            fa[T] = fa[u] = fa[v] = T;
        }
    }
    dfs (tot, 0);
}

struct Node {
    int pos, dis;

    bool operator < (Node rhs) const {
        return dis > rhs.dis;
    }
};

priority_queue <Node> q;

void dijkstra () {
    for (int i = 1; i <= n; ++i) dis[i] = i == 1 ? 0 : INF;
    q.push ((Node) {1, 0});
    while (!q.empty ()) {
        Node u = q.top (); q.pop ();
        if (dis[u.pos] < u.dis) continue;
        for (int i = G.head[u.pos]; i; i = G.e[i].nxt) {
            int v = G.e[i].to;
            if (dis[v] > dis[u.pos] + G.e[i].w) {
                dis[v] = dis[u.pos] + G.e[i].w;
                q.push ((Node) {v, dis[v]});
            }
        }
    }
} 

int query (int u, int p) {
    //u 出發節點 p 水位線
    for (int i = 19; i >= 0; --i) {
        if (_high[fafa[u][i]] > p) {
            u = fafa[u][i];
        }
    }
    // printf ("u = %d\n", u);
    return mindis[u];
}

void Init () {
    G.Init ();
    krus.Init ();
    memset (fafa, 0, sizeof (fafa));
    memset (_high, 0, sizeof (_high));
    memset (mindis, 0, sizeof (mindis));
}

int main () {
    //freopen ("data.in", "r", stdin);
    T = read ();
    while (T--) {
        Init ();
        printf ("T = %d\n", T);
        n = read (), m = read ();
        for (int i = 1; i <= m; ++i) {
            _e[i].u = read ();
            _e[i].v = read ();
            _e[i].l = read ();
            _e[i].a = read ();
            G.add_edge (_e[i].u, _e[i].v, _e[i].l);
            G.add_edge (_e[i].v, _e[i].u, _e[i].l); //建雙向邊
        }
        int lastans = 0;
        dijkstra ();
        kruskal ();
        Q = read (), K = read (), S = read ();
        for (int i = 1; i <= Q; ++i) {
            static int v, p, v0, p0;
            v0 = read (), p0 = read ();
            v = (v0 + K * lastans - 1) % n + 1;
            p = (p0 + K * lastans) % (S + 1);
            printf ("%d\n", lastans = query (v, p));
        }
    }
} 

Luogu P4768 [NOI2018]歸程