[NOI2018] 归程 题解

[NOI2018] 归程

[NOI2018] 归程

讲一下科技 K r u s c a l \tt Kruscal Kruscal 重构树的做法。

考虑到积水的问题,我们肯定是贪心选择积水深度最大的边进行建立重构树。那么这个就是一个小根堆。

之后考虑我们如何回答询问,对于一个询问的水深度,跳父亲。同时维护一下每个点的子树距离 1 1 1 节点距离最小的点即可。


对于 K r u s c a l \tt Kruscal Kruscal 重构树,每次根据 K r u s c a l \tt Kruscal Kruscal 的过程选出边,但是将边权变成点权之后链接原来边的两个点即可。

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

#define Fread
// #define Getmod

#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#endif // Fread

template <typename T>
void r1(T &x) {
	x = 0;
	char c(getchar());
	int f(1);
	for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
	x *= f;
}

template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
    r1(t);  r1(args...);
}

// #define int long long
const int maxn = 8e5 + 5;
const int maxm = maxn << 1;
const int N = 23;

int n, m;

int head[maxn], f[maxn][25];
int fa[maxn], cnt(0);
struct Edge {
    int to, next, w;
}edg[maxn << 1];
void add(int u,int v,int w) {
    edg[++ cnt] = (Edge) {v, head[u], w}, head[u] = cnt;
}

priority_queue<pair<int, int> > q;
int d[maxn], vis[maxn];
void DJ() {
    for(int i = 1; i <= n; ++ i) vis[i] = 0, d[i] = 0x3f3f3f3f;
    d[1] = 0; q.push(make_pair(0, 1));
    while(!q.empty()) {
        int u = q.top().second; q.pop();
        if(vis[u]) continue;
        vis[u] = 1;
        for(int i = head[u];i;i = edg[i].next) {
            int to = edg[i].to;
            if(d[to] > d[u] + edg[i].w) {
                d[to] = d[u] + edg[i].w;
                if(!vis[to]) q.push(make_pair(-d[to], to));
            }
        }
    }
}

struct Edgee {
    int u, v, a;
    int operator < (const Edgee &z) const {
        return a > z.a;
    }
}E[maxn];
struct Node {
    int l, a;
}t[maxn << 1];

int Q, K, S;
int lastans(0);
int getv() {
    int v; r1(v);
    return (v + K * lastans - 1) % n + 1;
}
int getp() {
    int p; r1(p);
    return (p + K * lastans) % (S + 1);
}

vector<int> vc[maxn];

int getfa(int x) {
    return x == fa[x] ? x : fa[x] = getfa(fa[x]);
}
int dep[maxn];
void dfs(int p,int pre) {
    f[p][0] = pre; dep[p] = dep[pre] + 1;
    for(int i = 1; i <= N; ++ i) f[p][i] = f[f[p][i - 1]][i - 1];
    for(vector<int> :: iterator it = vc[p].begin(); it != vc[p].end(); ++ it) {
        int to = *it;
        dfs(to, p);
        t[p].l = min(t[p].l, t[to].l);
    }
}

int query(int v,int p) {
    for(int i = N; ~ i; -- i) {
        // printf("%d %d\n", dep[v]);
        if(dep[v] - (1 << i) > 0) {
    // puts("CCC");
            if(t[f[v][i]].a > p) v = f[v][i];
        }
        // printf("i = %d\n", i);
    }
    return t[v].l;
}

void Kruscal() {
    int i;
    sort(E + 1, E + m + 1);
    for(i = 1; i <= (n << 1); ++ i) fa[i] = i;
    int tot(0);
    int ct(n);
    for(i = 1; i <= m; ++ i) {
        int u = E[i].u, v = E[i].v;
        int u1 = getfa(u), v1 = getfa(v);
        if(u1 == v1) continue;
        ++ ct;
        vc[ct].push_back(u1);
        vc[ct].push_back(v1);
        fa[u1] = fa[v1] = ct;
        t[ct].a = E[i].a;
        ++ tot;
        if(tot == n - 1) break;
    }
    dfs(ct, 0);
}

signed main() {
//    freopen("return5.in", "r", stdin);
//    freopen("S.out", "w", stdout);
	int i, j, T(1);
    r1(T);
    while(T --) {
        r1(n, m);
        for(i = 1; i <= m; ++ i) {
            int u, v, l, a; r1(u, v, l, a);
            E[i] = (Edgee) {u, v, a};
            add(u, v, l), add(v, u, l);
        }
        lastans = 0;
        DJ();
        // puts("ZZZ");
        for(i = 1; i <= (n << 1); ++ i) t[i].l = 2e9;
        for(i = 1; i <= n; ++ i) t[i].l = d[i];
        r1(Q, K, S);
        Kruscal();
        // puts("DDD");
        for(int _ = 1; _ <= Q; ++ _) {
            int v(getv());
            int p(getp());
            // printf("_ = %d\n", _);
            printf("%d\n", lastans = query(v, p));
        }

        cnt = 0;
        for(i = 1; i <= (n << 1); ++ i) fa[i] = head[i] = 0, vc[i].clear(), t[i].a = 0;
        for(i = 1; i <= (n << 1); ++ i) {
            for(j = 0; j <= N; ++ j) f[i][j] = 0;
        }
    }
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值