• BZOJ 2069: [POI2004]ZAW(Dijkstra + 二进制拆分)


    题意

    给定一个有 (N) 个点 (M) 条边的无向图, 每条无向边 最多只能经过一次 .

    对于边 ((u, v)) , 从 (u)(v) 的代价为 (a) , 从 (v)(u) 的代价为 (b) , 其中 (a)(b) 不一定相等.

    求一个包含 (1) 号点的有向环, 使得环上代价之和最小.

    (N le 3 imes 10^4 , M le 10^5 , 1 le a, b le 10^4) , 保证没有重边和自环 .

    题解

    考虑一条包含 (1) 的有向环, 一定是 (1 o x o cdots o y o 1) 这样子. ((x ot = y))

    那么我们可以考虑一个很显然的暴力:枚举 (x, y) 然后做最短路, 但是这样显然太慢了.

    但是这里的最短路是可以 “并行” 地求的. 也就是说, 如果给定两个不相交的点集 (mathcal{A}, mathcal{B}) , 那么我们可以用一次最短路的时间求出所
    有点对 ((x, y)) 满足 (x in mathcal{A}, y in mathcal{B}) 的最短路的最小值.

    具体地, 我们把 (1) 号点拆成两个点, 一个作为源点只连向 (mathcal{A}) 中的点, 另一个作为汇点只被 (mathcal{B}) 中的点连向.

    然后这里需要一个二进制拆分的技巧: 在与 (1) 相邻的那些点中,每次考虑它们二进制下的第 (k) 位, 将这一位为 (0) 的放入 (A) , 为 (1) 的放入 (mathcal{B}) , 那么只需 (log N) 次, 我们便可以考虑到每一对.

    以上全部摘自 __debug 的 PPT 。

    这个最短路可以用 Spfa 求,但实测要比 Dijkstra 慢几倍。。为了求稳,还是用 Dijkstra 吧233

    所以最后的复杂度就是 (mathcal O((N + M) log^2 N))

    总结

    对于一类考虑点对贡献,并且很多对可以并行求,且重复计算没有影响的问题,能考虑二进制拆分技巧,对于每一位分别考虑。

    将整体分成两组,最后计算贡献,能大幅度降低时间复杂度。

    新套路 get

    代码

    特别好写233

    #include <bits/stdc++.h>
     
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    #define debug(x) cout << #x << ": " << x << endl
    #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
    #define fir first
    #define sec second
    #define mp make_pair
     
    using namespace std;
     
    inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
     
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
        return x * fh;
    }
     
    void File() {
    #ifdef zjp_shadow
        freopen ("2069.in", "r", stdin);
        freopen ("2069.out", "w", stdout);
    #endif
    }
     
    const int N = 5010, M = 10100 * 2, inf = 0x7f7f7f7f;
     
    int Head[N], Next[M], to[M], val[M], e = 0;
     
    inline void add_edge(int u, int v, int w) { to[++ e] = v; Next[e] = Head[u]; Head[u] = e; val[e] = w; }
     
    priority_queue<pair<int, int> > P;
    int dis[N], S, T; bitset<N> vis;
     
    int Dijkstra() {
        Set(dis, inf); dis[S] = 0; P.push(mp(0, S)); vis.reset();
        while (!P.empty()) {
            int u = P.top().sec; P.pop(); if (vis[u]) continue ; vis[u] = true;
            for (int i = Head[u]; i; i = Next[i]) {
                int v = to[i]; if (chkmin(dis[v], dis[u] + val[i])) P.push(mp(- dis[v], v));
            }
        }
        return dis[T];
    }
     
    struct Edge { int u, v, a, b; } lt[M]; 
     
    int n, m;
    void Rebuild(int cur, int flag) {
        Set(Head, 0); e = 0; S = 1; T = n + 1;
     
        For (i, 1, m) {
            int u = lt[i].u, v = lt[i].v, a = lt[i].a, b = lt[i].b;
            if (u == 1) {
                if ((v & cur) ^ flag) add_edge(S, v, a); 
                 else add_edge(v, T, b);
            } else add_edge(u, v, a), add_edge(v, u, b);
        }
    }
     
    int main () {
     
        File();
     
        n = read(); m = read();
        For (i, 1, m) {
            int u = read(), v = read(), a = read(), b = read();
            if (u > v) swap(u, v), swap(a, b);
            lt[i] = (Edge) {u, v, a, b};
        }
     
        int ans = inf;
        for (int bit = 1; bit <= n; bit <<= 1) {
            Rebuild(bit, 0), chkmin(ans, Dijkstra());
            Rebuild(bit, bit), chkmin(ans, Dijkstra());
        }
     
        printf ("%d
    ", ans);
     
        return 0;
    }
    
  • 相关阅读:
    软件界面不是艺术作品
    关于c# winForm窗体最大化的设置
    表单中的重置与取消按钮
    一个汉字=2个英文字符么?我肤浅的这么认为。
    Linux在ASCII终端下显示彩色字体
    地震勘探原理名词解释
    Linux终端使用小技巧
    8个实用而有趣Bash命令提示行
    用ps改变图片分辨率,但是不改变图片大小,上一篇不大适用。
    禁用Win7自动更新后的重启提示
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9561449.html
Copyright © 2020-2023  润新知