Out on my own.|

园龄:粉丝:关注:

2024-10-09 21:42阅读: 9评论: 0推荐: 0

P6309 题解

很经典但是很好的题目。/qiang

标签:线段树。

  • 数轴上有一些关键点,不同的关键点可能在同一坐标。关键点的坐标均为整数。
  • 支持两种操作:
    1. 删去 / 添加一些关键点。
    2. 取一个点。使得它与 [l,r] 范围内所有关键点的距离最小。求最小距离。
  • 关键点的坐标数3×105所有坐标的绝对值109

首先每个坐标上有多少个关键点是很好维护的。如果已经取了个点,如何计算答案?一个经典做法是把绝对值拆开。假设取的点坐标是 x,左边有 a 个关键点,它们的坐标和为 Sa;右边有 b 个关键点,坐标和为 Sb。则答案为 (axSa)+(Sbbx)。用线段树维护 区间内关键点的个数坐标和 即可。

问题来到了如何找点 x。有一种比较感性的思考方式:随便取一个 x,我们将 x 向左移 Δx 且不越过关键点,那么距离增加了 bΔxaΔx=(ba)Δx。如果 a>b 即左边的点较多时,(ba)Δx<0,则距离减少了。也就是说,当左边的点多时,向左移是优的;同理当右边的点较多时,向右移是优的。那么最终 x 会停在区间内坐标从小到大第 点的个数2 个点的位置。求这个坐标可以线段树上二分。

用权值线段树维护即可。时间复杂度 O(nlogn)。我写的是动态开点线段树,获得了线段树写法的最优解。code:

#include <iostream>
#include <cstdio>
using namespace std;

typedef long long ll;
const int N = 300010, eps = 1e9, V = 1e9;
int cnt, ro;
struct Seg {
    int ls, rs;
    ll c, v;
} T[N * 4 * 32];
int n, m; ll a[N], b[N];
#define ls(k) (T[k].ls)
#define rs(k) (T[k].rs)

inline int read() {
	int x = 0, f = 1;
	char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
	return x * f;
}
inline void write(ll x) {
	if (x > 9) write(x / 10);
	putchar(x % 10 + 48);
}

void pushup(int k) {
    T[k].c = T[ls(k)].c + T[rs(k)].c;
    T[k].v = T[ls(k)].v + T[rs(k)].v;
}
void update(ll l, ll r, int &k, ll x, ll c) {
    if(!k) k = ++cnt;
    if(l == r) {
        T[k].c += c;
        T[k].v = x * T[k].c;
        return;
    }
    ll mid = (l + r) / 2;
    if(x <= mid) update(l, mid, ls(k), x, c);
    else update(mid + 1, r, rs(k), x, c);
    pushup(k);
}
ll getc(ll l, ll r, int k, ll L, ll R) {
    if(!k) return 0;
    if(L <= l && r <= R) return T[k].c;
    ll mid = (l + r) / 2, res = 0;
    if(L <= mid) res += getc(l, mid, ls(k), L, R);
    if(mid < R) res += getc(mid + 1, r, rs(k), L, R);
    return res;
}
ll getv(ll l, ll r, int k, ll L, ll R) {
    if(!k) return 0;
    if(L <= l && r <= R) return T[k].v;
    ll mid = (l + r) / 2, res = 0;
    if(L <= mid) res += getv(l, mid, ls(k), L, R);
    if(mid < R) res += getv(mid + 1, r, rs(k), L, R);
    return res;
}
ll find(ll l, ll r, int k, ll c) {
    if(l == r) return l;
    int mid = (l + r) / 2;
    if(c <= T[ls(k)].c) return find(l, mid, ls(k), c);
    else return find(mid + 1, r, rs(k), c - T[ls(k)].c);
}

int main() {
    // ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    n = read(), m = read();
    for (int i = 1; i <= n; i++) a[i] = read() + eps;
    for (int i = 1; i <= n; i++) {
        b[i] = read();
        update(0, 2e9, ro, a[i], b[i]);
    }
    while(m--) {
        int op = read();
        if(op == 2) {
            ll x = read(), y = read() + eps, z = read();
            update(0, 2e9, ro, a[x], -b[x]);
            a[x] = y, b[x] = z;
            update(0, 2e9, ro, a[x], b[x]);
        } else {
            ll l = read() + eps, r = read() + eps;
            ll mid = getc(0, 2e9, ro, 0, l - 1) + (getc(0, 2e9, ro, l, r) + 1) / 2;
            mid = find(0, 2e9, ro, mid);
            write(mid * getc(0, 2e9, ro, l, mid - 1) - getv(0, 2e9, ro, l, mid - 1) + getv(0, 2e9, ro, mid + 1, r) - mid * getc(0, 2e9, ro, mid + 1, r));
            puts("");
        }
    }
    return 0;
}

如有错误烦请您指出。

本文作者:Running-a-way

本文链接:https://www.cnblogs.com/Running-a-way/p/18455220

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Running_a_way  阅读(9)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起