• 「SNOI2019」通信


    传送门

    我们可以不难想到这样一种费用流做法:每个点拆成两个点,对于其中一类点 (u) ,我们直接连边 (s overset{1/0}{ o} u overset{1/W}{ o} t) 表示这个点直接连到控制中心。

    对于两个哨站 (i, j(j < i)) 的连边,我们就连边 (s overset{1/0}{ o} i overset{1/|a_i - a_j|}{ o} j^prime overset{1/0}{ o} t)

    然后跑一边费用流就好了。

    但是这样连边边数是 (O(n^2)) 的,显然过不了,我们就考虑优化连边。

    我们先考虑 (a_i > a_j) 的情况,(a_i le a_j) 的情况同理。

    由于我们有权值范围和区间范围的共同限制,考虑主席树优化连边,对于每次连边提取出来的 (log) 个区间,我们把 连入这些区间的边的费用设为 (a_i) ,最后从叶子连出的边费用设为 (-a_j) 这样就可以类似差分地算费用。

    需要注意的是,主席树可能常数会有点大,有可能会 ( ext{TLE}),建议用 ( ext{ZKW}) 费用流。

    参考代码:

    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    using namespace std;
    
    template < class T > void read(T& s) {
        s = 0; int f = 0; char c = getchar();
        while ('0' > c || c > '9') f |= c == '-', c = getchar();
        while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
        s = f ? -s : s;
    }
    
    typedef long long LL;
    const int _ = 1e6 + 5, __ = 2e6 + 5;
    
    int tot = 1, head[_]; struct Edge { int v, w, cs, nxt; } edge[__ << 1];
    void Add_edge(int u, int v, int w, int cs) { edge[++tot] = (Edge) { v, w, cs, head[u] }, head[u] = tot; }
    void Link(int u, int v, int w, int cs) { Add_edge(u, v, w, cs), Add_edge(v, u, 0, -cs); }
    
    int n, W, a[_], s, t, exi[_]; LL dis[_];
    int id, cnt, X[_], rt[2][_]; struct node { int lc, rc, id; } o[_ << 5];
    
    void build(int &p, int x, int to, int opt, int l = 1, int r = X[0]) {
        o[++cnt] = o[p], o[cnt].id = ++id, p = cnt;
        if (l == r) { Link(o[p].id, to, 1, opt ? X[l] : -X[l]); return ; }
        int mid = (l + r) >> 1;
        if (x <= mid) build(o[p].lc, x, to, opt, l, mid);
        else build(o[p].rc, x, to, opt, mid + 1, r);
        if (o[p].lc) Link(o[p].id, o[o[p].lc].id, 0x3f3f3f3f, 0);
        if (o[p].rc) Link(o[p].id, o[o[p].rc].id, 0x3f3f3f3f, 0);
    }
    
    void update(int p, int ql, int qr, int from, int v, int opt, int l = 1, int r = X[0]) {
        if (ql <= l && r <= qr) { Link(from, o[p].id, 1, opt ? -v : v); return ; }
        int mid = (l + r) >> 1;
        if (o[p].lc && ql <= mid) update(o[p].lc, ql, qr, from, v, opt, l, mid);
        if (o[p].rc && qr > mid) update(o[p].rc, ql, qr, from, v, opt, mid + 1, r);
    }
    
    void spfa() {
        static queue < int > Q;
        memset(dis, 0x3f, sizeof dis);
        dis[t] = 0, exi[t] = 1, Q.push(t);
        while (!Q.empty()) {
            int u = Q.front(); Q.pop(), exi[u] = 0;
            for (int i = head[u]; i; i = edge[i].nxt) {
                int v = edge[i].v, w = edge[i ^ 1].w, cs = edge[i ^ 1].cs;
                if (dis[v] > dis[u] + cs && w > 0) {
                    dis[v] = dis[u] + cs;
                    if (!exi[v]) exi[v] = 1, Q.push(v);
                }
            }
        }
    }
    
    int num, vis[_];
    
    int extend() {
        LL delta = 0x3f3f3f3f3f3f3f3f;
        for (int u = 0; u <= id; ++u) {
            if (vis[u] != num) continue ;
            for (int i = head[u]; i; i = edge[i].nxt) {
                int v = edge[i].v, w = edge[i].w, cs = edge[i].cs;
                if (vis[v] != num && w > 0)
                    delta = min(delta, dis[v] + cs - dis[u]);
            }
        }
        if (delta == 0x3f3f3f3f3f3f3f3f) return 0;
        for (int u = 0; u <= id; ++u) if (vis[u] == num) dis[u] += delta;
        return 1;
    }
    
    int dfs(int u, int flow) {
        vis[u] = num;
        if (u == t) return flow;
        for (int i = head[u]; i; i = edge[i].nxt) {
            int v = edge[i].v, w = edge[i].w, cs = edge[i].cs;
            if (dis[u] == dis[v] + cs && w > 0 && vis[v] != num) {
                int res = dfs(v, min(flow, w));
                if (res) { edge[i].w -= res, edge[i ^ 1].w += res; return res; }
            }
        }
        return 0;
    }
    
    LL zkw() {
        spfa();
        LL ans = 0;
        do {
            int f = 0;
            do ++num, f = dfs(s, 0x3f3f3f3f), ans += 1ll * dis[s] * f; while (f);
        } while (extend());
        return ans;
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
    #endif
        read(n), read(W), s = 0, t = 2 * n + 1, id = t;
        for (int i = 1; i <= n; ++i) read(a[i]), X[i] = a[i];
        for (int i = 1; i <= n; ++i) Link(s, i, 1, 0), Link(i, t, 1, W), Link(i + n, t, 1, 0);
        sort(X + 1, X + n + 1), X[0] = unique(X + 1, X + n + 1) - X - 1;
        for (int i = 1; i <= n; ++i) a[i] = lower_bound(X + 1, X + X[0] + 1, a[i]) - X;
        for (int i = 1; i <= n; ++i) {
            rt[0][i] = rt[0][i - 1], build(rt[0][i], a[i], i + n, 0);
            rt[1][i] = rt[1][i - 1], build(rt[1][i], a[i], i + n, 1);
        }
        for (int i = 2; i <= n; ++i) {
            update(rt[0][i - 1], 1, a[i], i, X[a[i]], 0);
            update(rt[1][i - 1], a[i] + 1, X[0], i, X[a[i]], 1);
        }
        printf("%lld
    ", zkw());
        return 0;
    }
    
  • 相关阅读:
    CSS 基本语法
    Html 块
    Html 表单
    Html 创建表格
    Html 建立超链接
    Html 在网页中使用图像
    CentOS7 重启问题
    Java 多线程——多线程的引入
    c++ 自然排序-window文件排序
    MFC更改图标
  • 原文地址:https://www.cnblogs.com/zsbzsb/p/13052112.html
Copyright © 2020-2023  润新知