P4768 [NOI2018] 归程 做题记录

听说这是个 Kruskal 重构树的练习题,于是往这方面想。

先预处理出最短路 \(dis\)

然后建出 Kruskal 重构树(枚举的边按照 \(h\) 从大到小排序)。

然后对于一个询问 \(v,p\)

\(v\) 不断向上跳,直到海拔 \(<p\) 为止,然后这个点的子树内的点都可到达。

于是我们可以找到这个子树内 \(dis\) 最小的点就是答案。

于是这题就做完了。

最短路用 \(\text{Dij}\) 或者 \(\text{SPFA}\)

Kruskal 重构树用并查集

\(v\) 向上跳用倍增。

子树内 \(dis\) 最小的点可以 ST 表或者线段树。

差不多 10:00 开始写的。

现在是 11:39,我还没调出来,我看看我什么时候调出来。

现在是 14:45,中间睡了两个小时的午觉,我调完了。

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 4e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct node {
    int fr, to, w, h;
    bool operator < (const node &b) const { return h > b.h; }
}E[MAXN];

struct Node {
    int id, val;
    bool operator < (const Node &b) const { return val > b.val;}
};

struct edge {
    int to, w, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge = 1;

int n, m, Q, K, S, Cnt;
int val[MAXN], fa[MAXN], Log2[MAXN];
int dis[MAXN], Dis[MAXN][21];
int siz[MAXN], dfn[MAXN], fath[MAXN][22], H[MAXN][21];
bool vis[MAXN];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

namespace Cut {
    struct edge { int to, nxt; }e[MAXN << 1];
    int head[MAXN], num_edge = 1;
    int cnt = 0;
    void add_edge(int from, int to) { e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }
    void dfs(int u, int fa) {
//        cout<<u<<"\n";
        siz[u] = 1, fath[u][0] = fa, dfn[u] = ++cnt;
        H[u][0] = fa <= n ? INF : val[fa]; Dis[cnt][0] = dis[u];
        for(int i = 1; i <= 20; ++i) {
            fath[u][i] = fath[fath[u][i - 1]][i - 1];
            H[u][i] = min(H[u][i - 1], H[fath[u][i - 1]][i - 1]);
        }
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if(v == fa) continue;
            dfs(v, u);
            siz[u] += siz[v];
        }
    }
}

void add_edge(int from, int to, int w) { e[++num_edge] = (edge){to, w, head[from]}, head[from] = num_edge; }
void Dij() {
    priority_queue<Node> q;
    memset(dis, 0x3f, sizeof dis);
    memset(vis, false, sizeof vis);
    q.push((Node){1, 0}), dis[1] = 0;
    while(!q.empty()) {
        int u = q.top().id; q.pop();
        if(vis[u]) continue;
        vis[u] = true;
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if(dis[v] > dis[u] + e[i].w) {
                dis[v] = dis[u] + e[i].w;
                if(!vis[v]) q.push((Node){v, dis[v]});
            }
        }
    }
}

int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void ExKruskal() {
    sort(E + 1, E + m + 1);
    for(int i = 1; i <= 2 * n; ++i) fa[i] = i;
    for(int i = 1; i <= m; ++i) {
        int u = E[i].fr, v = E[i].to, w = E[i].h;
        int uf = find(u), vf = find(v);
        if(uf != vf) {
            fa[uf] = fa[vf] = ++Cnt;
//            cout<<"edge: "<<Cnt<<" "<<uf<<" \n";
//            cout<<"edge: "<<Cnt<<" "<<vf<<" \n";
            Cut::add_edge(Cnt, uf), Cut::add_edge(Cnt, vf);
            val[Cnt] = w;
            if(Cnt == 2 * n - 1) break;
        }
    }
}

void Init() {
    for(int i = 1; i <= 20; ++i) {
        for(int j = 1; j + (1 << i) - 1 <= Cut::cnt; ++j) {
            Dis[j][i] = min(Dis[j][i - 1], Dis[j + (1 << i - 1)][i - 1]);
        }
    }
}

void Clear() {
    memset(head, false, sizeof head);
    memset(Cut::head, false, sizeof Cut::head);
    num_edge = Cut::num_edge = 1;
    memset(Dis, 0x3f, sizeof Dis);
    memset(val, false, sizeof val);
    memset(fath, false, sizeof fath);
    memset(H, false, sizeof H);
    Cut::cnt = 0;
}

signed main()
{
    int T = read();
    for(int i = 2; i <= 400000; ++i) Log2[i] = Log2[i >> 1] + 1;
    while(T--) {
        Clear();
        n = read(), m = read();
        Cnt = n;
        for(int i = 1; i <= m; ++i) {
            E[i].fr = read(), E[i].to = read(), E[i].w = read(), E[i].h = read();
            add_edge(E[i].fr, E[i].to, E[i].w), add_edge(E[i].to, E[i].fr, E[i].w);
        }
        Dij();
        ExKruskal();
        Cut::dfs(Cnt, 0);
        Init();
//        cout<<"dis: ";
//        for(int i = 1; i <= n; ++i) cout<<dis[i]<<" "; puts("");
//        for(int i = 1; i <= Cut::cnt; ++i) cout<<dfn[i]<<" "; puts("");
//        for(int i = 1; i <= Cut::cnt; ++i) cout<<Dis[i][0]<<" "; puts("");
        Q = read(), K = read(), S = read();
        for(int i = 1, v, p, lst = 0; i <= Q; ++i) {
            v = read(), p = read();
            v = (v + K * lst - 1) % n + 1, p = (p + K * lst) % (S + 1);
//            cout<<"v p: "<<v<<" "<<p<<" "<<"\n";
            int x = v;
            for(int i = 20; i >= 0; --i) {
//                cout<<"jump: "<<x<<" "<<i<<" "<<fath[x][i]<<" "<<H[x][i]<<" \n";
                if(fath[x][i] && H[x][i] > p) {
//                    cout<<i<<" \n";
                    x = fath[x][i];
                }
            }
            int len = siz[x], l = dfn[x], r = dfn[x] + siz[x] - 1;
//            cout<<x<<" "<<fath[x][0]<<" "<<H[x][0]<<" "<<l<<" "<<r<<" "<<"\n";
            lst = min(Dis[l][Log2[len]], Dis[r - (1 << Log2[len]) + 1][Log2[len]]);
            printf("%lld\n", lst);
        }
    }
    return 0;
}

好久没调过这么长的代码了,记得上次调的时候还是在上次。

这道题好像就是 SPFA 之墓,所以我用的 Dij。/cy

ST 表虽然没怎么用过,但显然比线段树好写。

码力变强之后调题更快了,对于一些板子更自信了,所以我错哪了呢?

  • 定义了 lst 没有用;
  • 解密的公式写错了;
  • 题意理解错了,海拔严格高于才能走,代码一开始写的是不低于。
posted @ 2021-09-14 17:55  Suzt_ilymtics  阅读(40)  评论(0)    收藏  举报