[APIO2015]八邻旁之桥
嘟嘟嘟
这题拿到后就瞬秒\(k = 1\)的情况,跟这题一模一样。
所以我最后也只拿了31分……
正解要推出这么个性质,就是如果只有一座桥,那么所有城市和办公室的中位数就是这座桥的位置(然而我的\(k = 1\)做法显然没有用到这一点……)。
怎么证明呢?
先用式子表示一下:就是我们要找到一个\(x\),满足\(\sum_{i = 1} ^ {m} |L_i - x| + |R_i - x|\)最小,即\(\sum _ {i = 1} ^ {2m} |a_i - x|\)最小。
假设\(x\)现在是中位数,那么如果我们把\(x\)变小或变大了一位(这里的“一位”指的是和某一个数的大小关系发生了改变),带来的代价就是\(m * \Delta x - (m - 1) * \Delta x = \Delta x\),这个值显然大于0,所以移动必定会带来正的代价。那么中位数就是最优的。
那么\(k = 2\)的情况怎么办呢?
观察发现,每一个人的距离和\(|L_i - R_i|\)以及距离桥的距离有关。因此我们取\(L_i, R_i\)的中点,看中点离哪边的桥近就走哪边。
所以我们先把所有人按各自的中点排序(即\(L_i + R_i\)),然后枚举两座桥的分界点,即分界点左侧的人都上一座桥,右侧的人上另一座桥。然后两边就变成了\(k = 1\)的情况,分别求中位数即可。
因为要支持动态删点和加点,所以开两棵权值线段树,支持查找第\(k\)大和区间求和操作。
找完中位数后计算答案的方法和上面说的那道题一样,用前缀和整体相减即可。
然后我因为按离散化后的值的中位数排序gg了好半天。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const ll INF = 1e18;
const db eps = 1e-8;
const int maxn = 1e5 + 5;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int K, n;
char s1[2], s2[2];
int a[maxn], b[maxn], t[maxn << 1], cnt[2];
ll suma[maxn], sumb[maxn];
In void work0()
{
ll ans = INF, SUM = 0;
for(int i = 1; i <= n; ++i)
{
scanf("%s", s1); int val1 = read();
scanf("%s", s2); int val2 = read();
if(s1[0] == s2[0]) SUM += abs(val1 - val2);
else
{
a[++cnt[0]] = val1, b[cnt[0]] = val2;
if(s1[0] == 'B') swap(a[cnt[0]], b[cnt[0]]);
}
}
for(int i = 1; i <= cnt[0]; ++i) t[i] = a[i];
for(int i = 1; i <= cnt[0]; ++i) t[i + cnt[0]] = b[i];
sort(t + 1, t + (cnt[0] << 1) + 1);
cnt[1] = unique(t + 1, t + (cnt[0] << 1) + 1) - t - 1;
for(int i = 1; i <= cnt[0]; ++i) a[i] = lower_bound(t + 1, t + cnt[1] + 1, a[i]) - t;
for(int i = 1; i <= cnt[0]; ++i) b[i] = lower_bound(t + 1, t + cnt[1] + 1, b[i]) - t;
sort(a + 1, a + cnt[0] + 1), sort(b + 1, b + cnt[0] + 1);
for(int i = 1; i <= cnt[0]; ++i) suma[i] = suma[i - 1] + t[a[i]];
for(int i = 1; i <= cnt[0]; ++i) sumb[i] = sumb[i - 1] + t[b[i]];
suma[cnt[0] + 1] = suma[cnt[0]];
sumb[cnt[0] + 1] = sumb[cnt[0]];
for(int i = 1, pos; i <= cnt[1]; ++i)
{
ll sum = 0;
pos = lower_bound(a + 1, a + cnt[0] + 1, i) - a;
if(pos > 0) sum = 1LL * t[i] * (pos - 1) - suma[pos - 1] + suma[cnt[0]] - suma[pos - 1] - 1LL * t[i] * (cnt[0] - pos + 1);
pos = lower_bound(b + 1, b + cnt[0] + 1, i) - b;
if(pos > 0) sum += 1LL * t[i] * (pos - 1) - sumb[pos - 1] + sumb[cnt[0]] - sumb[pos - 1] - 1LL * t[i] * (cnt[0] - pos + 1);
ans = min(ans, sum);
}
if(ans == INF) ans = 0;
write(ans + SUM + cnt[0]), enter;
}
struct Node
{
int L, R;
In bool operator < (const Node& oth)const
{
return L + R < oth.L + oth.R;
}
}q[maxn];
int qcnt = 0, tot = 0;
struct Tree
{
int l[maxn << 3], r[maxn << 3], siz[maxn << 3];
ll sum[maxn << 3];
In void build(int L, int R, int now)
{
l[now] = L, r[now] = R;
sum[now] = siz[now] = 0;
if(L == R) return;
int mid = (L + R) >> 1;
build(L, mid, now << 1);
build(mid + 1, R, now << 1 | 1);
}
In void pushup(int now)
{
sum[now] = sum[now << 1] + sum[now << 1 | 1];
siz[now] = siz[now << 1] + siz[now << 1 | 1];
}
In void update(int id, int now, int flg)
{
if(l[now] == r[now])
{
sum[now] += 1LL * t[id] * flg;
siz[now] += flg; return;
}
int mid = (l[now] + r[now]) >> 1;
if(id <= mid) update(id, now << 1, flg);
else update(id, now << 1 | 1, flg);
pushup(now);
}
In ll kth(int k, int now)
{
if(l[now] == r[now]) return t[l[now]] * k;
if(siz[now << 1] >= k) return kth(k, now << 1);
else return siz[now << 1] + kth(k - siz[now << 1], now << 1 | 1);
}
In ll query_low(int k, int now, db d)
{
if(l[now] == r[now]) return d * k - 1LL * t[l[now]] * k;
if(siz[now << 1] > k) return query_low(k, now << 1, d);
else if(siz[now << 1] == k) return d * siz[now << 1] - sum[now << 1];
else return d * siz[now << 1] - sum[now << 1] + query_low(k - siz[now << 1], now << 1 | 1, d);
}
In ll query_upp(int k, int now, db d)
{
if(l[now] == r[now]) return 1LL * t[l[now]] * k - d * k;
if(siz[now << 1 | 1] > k) return query_upp(k, now << 1 | 1, d);
else if(siz[now << 1 | 1] == k) return sum[now << 1 | 1] - d * siz[now << 1 | 1];
else return sum[now << 1 | 1] - d * siz[now << 1 | 1] + query_upp(k - siz[now << 1 | 1], now << 1, d);
}
In ll Query(int len)
{
if(!len) return 0;
ll mid = kth(len >> 1, 1);
return query_low(len >> 1, 1, mid) + query_upp(len >> 1, 1, mid);
}
}t1, t2;
In void work1()
{
ll ans = INF, SUM = 0;
for(int i = 1; i <= n; ++i)
{
scanf("%s", s1); int val1 = read();
scanf("%s", s2); int val2 = read();
if(s1[0] == s2[0]) SUM += abs(val1 - val2);
else q[++qcnt] = (Node){val1, val2}, t[++tot] = val1, t[++tot] = val2;
}
if(!qcnt) {write(SUM), enter; return;}
sort(t + 1, t + tot + 1);
tot = unique(t + 1, t + tot + 1) - t - 1;
t1.build(1, tot, 1), t2.build(1, tot, 1);
sort(q + 1, q + qcnt + 1);
for(int i = 1; i <= qcnt; ++i)
{
q[i].L = lower_bound(t + 1, t + tot + 1, q[i].L) - t;
q[i].R = lower_bound(t + 1, t + tot + 1, q[i].R) - t;
t2.update(q[i].L, 1, 1), t2.update(q[i].R, 1, 1);
}
for(int i = 1; i <= qcnt + 1; ++i)
{
ll sum = t1.Query((i - 1) << 1) + t2.Query((qcnt - i + 1) << 1);
ans = min(ans, sum);
if(i > qcnt) break;
t1.update(q[i].L, 1, 1), t1.update(q[i].R, 1, 1);
t2.update(q[i].L, 1, -1), t2.update(q[i].R, 1, -1);
}
write(ans + SUM + qcnt), enter;
}
int main()
{
K = read(), n = read();
if(K == 1) {work0(); return 0;}
if(K == 2) {work1(); return 0;}
return 0;
}