bzoj5415: [Noi2018]归程 kruskal

bzoj5415: [Noi2018]归程

题目描述:www.lydsy.com/JudgeOnline/upload/noi2018day1.pdf

分析

NoiD1T1,网络同步赛的时候搞了一个离线正解+在线暴力的75分,居然完全没有意识到这题是kruskal重构树。
想想之前其实也就做过一道bzoj3551

kruskal重构树

介绍一下kruskal重构树算法。
针对的是一张图上的最小生成树。
具体流程就是在搞kruskal算法的时候,把边变成新建的点,两个堆顶连接到这条边上,这个新的点的权值为边权,最终会形成一颗二叉树,这棵树有非常多优秀的性质。

1.树上除叶子结点以外的点都对应着原来生成树中的边,叶子结点就是原来生成树上的节点。
2.由于新点的创建顺序与原来生成树上边权的大小有关,可以发现,从每个点到根节点上除叶子结点外按顺序访问到的点的点权是单调的。
3.出于kruskal算法贪心的性质,两个点u和v的lca的点权就对应着它们最小生成树上的瓶颈。
4.实际上这棵树就是一个二叉堆

一般用来解决带有偏序性质边权图的连通性问题。
主要是利用了二叉堆的性质。

例题分析

对于这道题,显然可以转化成被淹完之后当前点所在联通块内所有点到终点距离最小值。
首先Dij一遍,那么实际上只需要快速维护出剩下边权大于某个值的边组成的图的联通情况。
以海拔为关键字建出Kruskal重构树,树上跑倍增可以快速找出最大的联通块,建树的时候顺便维护一下子树信息即可。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
const int N = 4e5 + 10, Nt = 262143; const int inf = 0x3f3f3f3f;
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int to[N << 1], nx[N << 1], w[N << 1], pr[N], v[N], g[N], f[N][20], D[N << 1], n, m, tp;
void add(int u, int v, int ww) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp; w[tp] = ww;}
void adds(int u, int v, int w) {add(u, v, w); add(v, u, w);}
struct P {int u, x;}T[Nt << 1];
P min(P a, P b) {return a.x < b.x ? a : b;}
struct E {
    int u, v, l, a;
    void In() {u = ri(); v = ri(); l = ri(); a = ri(); adds(u, v, l);}
}e[N];
bool cmp(E a, E b) {return a.a > b.a;}
void Up(int i, int v) {for(T[i += Nt].x = v; i >>= 1; T[i] = min(T[i << 1], T[i << 1 | 1])) ;}
void Dij() {
    std::memset(T, 0x3f, sizeof(T));
    std::memset(D, 0x3f, sizeof(D));
    for(int i = 1;i <= n; ++i) T[i + Nt].u = i;
    for(Up(1, D[1] = 0); T[1].x != inf;) {
        int u = T[1].u; Up(u, inf);
        for(int i = pr[u]; i; i = nx[i])
            if(D[to[i]] > D[u] + w[i])  
                Up(to[i], D[to[i]] = D[u] + w[i]);
    }
}
void Dfs(int u, int fa) {
    f[u][0] = fa; for(int i = 1; i <= 19; ++i) f[u][i] = f[f[u][i - 1]][i - 1];
    for(int i = pr[u]; i; i = nx[i]) Dfs(to[i], u), D[u] = std::min(D[u], D[to[i]]);
}
int F(int x) {return !g[x] ? x : g[x] = F(g[x]);}
void Kru() {
    std::sort(e + 1, e + m + 1, cmp); int tot = 0, cnt = n;
    for(int i = 1;i <= n; ++i) pr[i] = 0; tp = 0;
    for(int i = 1;i <= m; ++i) {
        int fu = F(e[i].u), fv = F(e[i].v);
        if(fu != fv) {
            add(++cnt, fu, 0); add(cnt, fv, 0); v[cnt] = e[i].a; 
            g[fu] = cnt, g[fv] = cnt; ++tot;
        }
        if(tot == n - 1) break;
    }
    Dfs(cnt, 0);
    for(int i = 1;i <= cnt; ++i) g[i] = pr[i] = 0; tp = 0;
}
int Que(int u, int w) {
    for(int i = 19; ~i; --i) if(f[u][i] && v[f[u][i]] > w) u = f[u][i];
    return D[u];
}
int main() {
    for(int T = ri(); T--; ) {
        n = ri(); m = ri();
        for(int i = 1; i <= m; ++i) e[i].In();
        Dij(); Kru();
        int Q = ri(), K = ri(), S = ri();
        for(int v, p, la = 0;Q--;) {
            v = (ri() + K * la - 1) % n + 1;
            p = (ri() + K * la) % (S + 1);
            printf("%d\n", la = Que(v, p));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/81299234