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号