2025.04.19 CW 模拟赛 C. 恋爱入门教学
C. 恋爱入门教学
题目描述
假定当前有 \(k\) 个三元组 \((F_i, T_i, B_i)\),最小化:
\[\sum_{i=1}^k |T_i(F_i - f) + B_i|
\]
思路
考虑变换一下式子
\[\begin{aligned}
& \sum_{i = 1}^k |T_i(F_i - f) + B_i| \\
& = \sum_{i = 1}^k |T_i| \cdot |(F_i + \frac{B_i}{T_i}) - f|
\end{aligned}
\]
可以发现 \(|T_i| \cdot |(F_i + \frac{B_i}{T_i}) - f|\) 相当于在数轴上 \(F_i + \frac{B_i}{T_i}\) 的位置添加 \(|T_i|\) 个点, 最后求所有点到某一个点的距离和的最小值. 根据初中数学知识可以知道当 \(f\) 取所有数的中位数是最优的.
在实现上, 由于可能涉及到小数, 所以我们选择离线后离散化数据, 查询中位数时使用线段树上二分即可.
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
char buf[1 << 20], *p1, *p2;
#define getchar() (p1 == p2 and (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? 0 : *p1++)
int read() {
int x = 0; bool f = 0;
char c = getchar();
while (c < '0' or c > '9') {
f |= c == '-';
c = getchar();
}
while (c >= '0' and c <= '9') {
x = x * 10 + (c & 15);
c = getchar();
}
return f ? -x : x;
}
constexpr int N = 500001;
int n, len, id[N], P = 1, DEP = 0;
ll tr[N * 3];
double tmp[N], val[N * 3];
struct Node {
int opt, f, t, b, pos;
double val;
} a[N];
class SegmentTree {
private:
public:
void update(int x, int k) {
int p = x + P;
tr[p] += k, val[p] += k * tmp[x];
for (p >>= 1; p; p >>= 1) {
tr[p] = tr[p << 1] + tr[p << 1 | 1];
val[p] = val[p << 1] + val[p << 1 | 1];
}
}
int find(int k) {
int l = 1, dep = 0;
while (dep < DEP) {
if (tr[l << 1] >= k) {
l = l << 1;
}
else {
k -= tr[l << 1];
l = l << 1 | 1;
}
++dep;
}
return l - P;
}
ll query1(int l, int r) {
l += P - 1, r += P + 1;
ll res = 0;
while (l ^ 1 ^ r) {
~l & 1 and (res += tr[l ^ 1]);
r & 1 and (res += tr[r ^ 1]);
l >>= 1, r >>= 1;
}
return res;
}
double query2(int l, int r) {
l += P - 1, r += P + 1;
double res = 0;
while (l ^ 1 ^ r) {
~l & 1 and (res += val[l ^ 1]);
r & 1 and (res += val[r ^ 1]);
l >>= 1, r >>= 1;
}
return res;
}
} seg;
void init() {
n = read();
for (int i = 1; i <= n; ++i) {
a[i].opt = read();
if (a[i].opt & 1) {
a[i].f = read(), a[i].t = read(), a[i].b = read();
if (!a[i].t) {
tmp[++len] = 1e18;
}
else {
a[i].val = tmp[++len] = a[i].f + 1.0 * a[i].b / a[i].t;
}
a[i].t = abs(a[i].t), id[len] = i;
}
else {
a[i].f = read();
}
}
sort(tmp + 1, tmp + len + 1);
for (int i = 1; i <= n; ++i) {
if (a[i].opt == 2) {
continue;
}
a[i].pos = lower_bound(tmp + 1, tmp + len + 1, a[i].val) - tmp;
}
while (P <= len + 1) {
P <<= 1, ++DEP;
}
}
void calculate() {
ll sum0 = 0;
for (int i = 1; i <= n; ++i) {
if (a[i].opt & 1) {
if (!a[i].t) {
sum0 += abs(a[i].b);
}
else {
seg.update(a[i].pos, a[i].t);
}
}
else {
int x = id[a[i].f];
if (!a[x].t) {
sum0 -= abs(a[i].b);
}
else {
seg.update(a[x].pos, -a[x].t);
}
}
int pos = seg.find((tr[1] + 1) >> 1);
double res = tmp[pos] * (seg.query1(1, pos) - seg.query1(pos + 1, len));
res += seg.query2(pos + 1, len) - seg.query2(1, pos) + sum0;
printf("%.6lf\n", res);
}
}
void solve() {
init();
calculate();
}
int main() {
solve();
return 0;
}

浙公网安备 33010602011771号