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