「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;
}