「SNOI2019」通信

传送门

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

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

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

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

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

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

需要注意的是,主席树可能常数会有点大,有可能会 \(\text{TLE}\),建议用 \(\text{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\n", zkw());
    return 0;
}
posted @ 2020-06-05 20:07  Sangber  阅读(135)  评论(0编辑  收藏  举报