zoukankan      html  css  js  c++  java
  • 线段树优化建图

    线段树优化建图经常用来优化单点向某个区间连边,区间向单点连边的问题,可以将边数从 (qn) 级别降至 (q log n) 级别。

    具体地,从一道题来引入:CF786B Legacy

    先单独考虑从点 (u) 向区间 ([l, r]) 连边的操作。

    你会发现因为任意一个区间 ([l, r]) 在线段树上都可以被表示为 (log n) 个区间,我们可以尝试从 (u) 向这 (log n) 个区间直接连边,然后像加法一样打上一个懒标记。

    那么问题就在于如何下传这个懒标记,你会发现如果 (u) 可以走到 ([l, r]) 这个区间,那么一定可以走到 ([l, r]) 在线段树上的子区间。

    因此,我们可以事先将线段树上的每个区间向其子区间连一条有向边,那么懒标记的问题也就不存在了。

    再来考虑从区间 ([l, r]) 向单点 (u) 连边的操作。

    同样地,我们新开一颗线段树,每次将区间 ([l, r]) 在线段树上对应的 (log n) 个区间向 (u) 连一条有向边。

    但由于现在变成了一个区间的儿子区间都能走出去,因此我们预先需要从儿子区间向父亲区间连一条有向边。

    但是你会发现我们的单点 (u) 是还未定义的,还需要考虑这最后一点。

    一个最简单的想法就是直接开一排的点 (1 sim n),然后将这上述两个操作连到这 (n) 个点上。

    于此同时,因为实际上在我们的构造中出现了 (3) 个点均代表原图上的同一个点,但构造中这 (3) 个可能可以到达不同点。

    因此,我们还需要将这三个点联通。

    但实际上是不需要这样的,不难发现单点也可以被认为是线段树上的一个区间,因此可以直接用两个线段树的底层代表单点。

    同时,还是需要保证单点在图上的一致性,因此还需要将两个单点之间连上一条无向边。

    #include <bits/stdc++.h>
    using namespace std;
    #define ls t[p].l
    #define rs t[p].r
    #define mid (l + r >> 1)
    #define rep(i, l, r) for (int i = l; i <= r; ++i)
    #define Next(i, u) for (int i = h[u]; i; i = e[i].next)
    const int N = 400000 + 5;
    const int M = 30 + 5;
    const long long inf = 1e14;
    struct edge { int v, next, w;} e[N * M];
    struct tree { int l, r;} t[N];
    struct node { 
        int p; long long w;
        bool operator < (const node &x) const {
            return w > x.w;
        }
    };
    bool book[N];
    priority_queue <node> Q;
    long long dis[N];
    int n, q, l, r, u, v, w, s, opt, tot, cnt, rt[2], h[N], pl[2][N];
    int read() {
        char c; int x = 0, f = 1;
        c = getchar();
        while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
        while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    void add(int u, int v, int w, int type) { 
        if(!type) e[++tot].v = v, e[tot].w = w, e[tot].next = h[u], h[u] = tot;
        else e[++tot].v = u, e[tot].w = w, e[tot].next = h[v], h[v] = tot;
    }
    void build(int &p, int l, int r, int type) {
        p = ++cnt; if(l == r) { pl[type][l] = p; return;}
        build(ls, l, mid, type), build(rs, mid + 1, r, type);
        add(p, ls, 0, type), add(p, rs, 0, type);
    }
    void update(int p, int l, int r, int x, int y, int k, int w, int type) {
        if(l >= x && r <= y) { add(k, p, w, type); return;}
        if(mid >= x) update(ls, l, mid, x, y, k, w, type);
        if(mid < y) update(rs, mid + 1, r, x, y, k, w, type);
    }
    signed main() {
        n = read(), q = read(), s = read();
        build(rt[0], 1, n, 0), build(rt[1], 1, n, 1);
        rep(i, 1, n) add(pl[0][i], pl[1][i], 0, 0), add(pl[0][i], pl[1][i], 0, 1);
        rep(i, 1, q) {
            opt = read();
            if(opt == 1) u = read(), v = read(), w = read(), update(rt[0], 1, n, v, v, pl[1][u], w, 0);
            if(opt == 2) u = read(), l = read(), r = read(), w = read(), update(rt[0], 1, n, l, r, pl[1][u], w, 0);
            if(opt == 3) u = read(), l = read(), r = read(), w = read(), update(rt[1], 1, n, l, r, pl[0][u], w, 1);
        }
        rep(i, 1, cnt) dis[i] = inf;
        Q.push((node){pl[1][s], 0}), dis[pl[1][s]] = 0;
        while (!Q.empty()) {
            node u = Q.top(); Q.pop();
            if(book[u.p]) continue; book[u.p] = true;
            Next(i, u.p) {
                int v = e[i].v; 
                if(dis[v] > dis[u.p] + e[i].w) dis[v] = dis[u.p] + e[i].w, Q.push((node){v, dis[v]});
            }
        }
        rep(i, 1, n) printf("%lld ", dis[pl[0][i]] == inf ? -1 : dis[pl[0][i]]);
        return 0;
    }
    

    值得一提的是,线段树的优秀性质使得它能解决很大一部分设计区间的问题。

    比如:

    • 一个区间能在线段树上用 (log n) 个区间表示,这类问题通常拥有区间的包含性。换句话说,父亲区间满足的信息子区间也满足。

    • 线段树的可并性,这给一类单点修改问题很大的契机。比如:楼房重建

    • 线段总长度 (O(n log n)),这给一类区间存在性问题很暴力的解法。即通常某个信息存在于某个区间,可以暴力插入到线段树上的对应区间,查询就只需要访问 (log n) 个区间或最后一起访问。

    GO!
  • 相关阅读:
    ASP.NET 表单验证 Part.1(理解表单验证)
    Silverlight 简介 Part.3(设计 Siverlight 页面)
    ASP.NET 成员资格 Part.3(LoginStatus、LoginView、PasswordRecovery)
    ASP.NET 网站部署 Part.1(安装IIS、复制文件部署网站)
    ASP.NET Dynamic Data Part.1(创建动态数据应用程序)
    ASP.NET 安全模型 Part.2(SSL)
    ASP.NET MVC Part.2(扩展基本的 MVC 应用程序)
    ASP.NET 网站部署 Part.2(使用 Web 部署)
    开发高级 Web 部件
    创建 Web 部件(WebPart 类、简单的 Web 部件)
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13832890.html
Copyright © 2011-2022 走看看