[APIO2015]八鄰旁之橋
阿新 • • 發佈:2020-08-17
注意到 \(K = 1, 2\) 於是我們可以從簡單的 \(K = 1\) 開始入手。可以發現家和辦公室在同一邊的人不管建不建橋都是無所謂的,因此下面我們只需要考慮不在同一邊的人,假設橋的位置在 \(d\) 那麼答案可以簡單的表示為:
\[\sum\limits_{i = 1} ^ n |S_i - d| + |T_i - d| \]
這可以看作是 \(2n\) 個在數軸上的點到 \(d\) 的距離,這是一個很經典的問題,只需取這 \(2n\) 個點的中位數即可。
接下來再考慮 \(K = 2\) 的情況,如果我們想直接求出兩座橋的位置是很困難的,但是可以發現這樣一件事,在兩個相鄰的點之間任意一個點建第一座橋的答案都將是一樣的,那麼這意味著我們第一座橋的有效位置只有 \(2n\)
有了上面這個人選橋的最優方案,不難發現最終所有人(按中點從小到大排序)選橋的方案一定是左邊的人選一座橋,右邊的人選另一座橋。那麼左右兩邊橋的位置我們就可以使用 \(K = 1\)
\[\sum\limits_{x_i > d} x_i - d + \sum\limits_{x_i < d} d - x_i \]
於是我們需要一個能查詢一組數的中位數,查詢比某個數小的數有多少個,以及這些數的權值為多少的資料結構,權值線段樹是一個好的選擇。
#include<bits/stdc++.h> using namespace std; #define ls (p << 1) #define rs (p << 1 | 1) #define mid (l + r) / 2 #define rep(i, l, r) for(int i = l; i <= r; ++i) typedef long long ll; const int N = 200000 + 5; const ll inf = 10000000000000000; ll ans; char P[2], Q[2]; int n, m, K, S, T, d[N]; struct tree{ int cnt[N << 2]; ll sum[N << 2]; void update(int p, int l, int r, int x, int y, int k){ if(l >= x && r <= y){ sum[p] += 1ll * d[l] * k, cnt[p] += k; return;} if(mid >= x) update(ls, l, mid, x, y, k); if(mid < y) update(rs, mid + 1, r, x, y, k); sum[p] = sum[ls] + sum[rs], cnt[p] = cnt[ls] + cnt[rs]; } ll query(int p, int l, int r, int k){ if(l == r) return 1ll * k * d[l]; if(cnt[ls] >= k) return query(ls, l, mid, k); // 開始這裡複製 find 時忘記改了,調了一年 else return sum[ls] + query(rs, mid + 1, r, k - cnt[ls]); } int find(int p, int l, int r, int k){ if(l == r) return d[l]; if(cnt[ls] >= k) return find(ls, l, mid, k); else return find(rs, mid + 1, r, k - cnt[ls]); } }T1, T2; struct node{ int l, r; }a[N], b[N]; int read(){ char c; int x = 0, f = 1; c = getchar(); while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } namespace S1{ int cnt = 0, c[N]; void solve(){ rep(i, 1, n) c[++cnt] = a[i].l, c[++cnt] = a[i].r; sort(c + 1, c + cnt + 1); rep(i, 1, cnt) ans += abs(c[i] - c[n]); printf("%lld", ans); } } namespace S2{ int tot = 0; long long tmp = inf, res = 0, Sl = 0, Sr = 0; bool cmp(node a, node b){ return a.l + a.r < b.l + b.r; } void solve(){ sort(a + 1, a + n + 1, cmp); rep(i, 1, n) d[++tot] = a[i].l, d[++tot] = a[i].r; sort(d + 1, d + tot + 1); tot = unique(d + 1, d + tot + 1) - d - 1; rep(i, 1, n){ b[i].l = lower_bound(d + 1, d + tot + 1, a[i].l) - d; b[i].r = lower_bound(d + 1, d + tot + 1, a[i].r) - d; } rep(i, 1, n) Sr += a[i].l, Sr += a[i].r; rep(i, 1, n) T2.update(1, 1, tot, b[i].l, b[i].l, 1), T2.update(1, 1, tot, b[i].r, b[i].r, 1); rep(i, 1, n){ T1.update(1, 1, tot, b[i].l, b[i].l, 1), T1.update(1, 1, tot, b[i].r, b[i].r, 1); T2.update(1, 1, tot, b[i].l, b[i].l, -1), T2.update(1, 1, tot, b[i].r, b[i].r, -1); Sl += a[i].l, Sl += a[i].r, Sr -= a[i].l, Sr -= a[i].r; int P1 = T1.find(1, 1, tot, i), P2 = T2.find(1, 1, tot, n - i); ll S1 = T1.query(1, 1, tot, i), S2 = T2.query(1, 1, tot, n - i); res = 1ll * P1 * i - S1 + Sl - S1 - 1ll * P1 * i; res += 1ll * P2 * (n - i) - S2 + Sr - S2 - 1ll * P2 * (n - i); tmp = min(tmp, res); } printf("%lld", ans + (tmp == inf ? 0 : tmp)); } } int main(){ K = read(), m = read(); int num = 0; rep(i, 1, m){ scanf("%s", P + 1), S = read(), scanf("%s", Q + 1), T = read(); if(P[1] == Q[1]) ans += abs(S - T); else a[++num] = (node){S, T}, ++ans; } n = num; if(K == 1) S1 :: solve(); else S2 :: solve(); return 0; }