题解 P6309
树状数组写法,两只
这是一个类似货仓选址的问题,对于每个询问,我们求出整个区间一共住着多少人
那么怎么统计答案呢?
设避难所的位置为
所以我们只需要维护区间
代码很短,只有 60 多行。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read() {
int s = 0,f = 1;
char ch = getchar();
while (!isdigit(ch)) f = ch == '-' ? -1 : 1, ch = getchar();
while (isdigit(ch)) s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
ll c[900010],c2[900010];//维护vi,vixi的和
int x[300010],v[300010],h[1200010],n,m,tot;
void upd(int x,int s,int ss) {
for (;x <= tot;x += (x & -x)) c[x] += s, c2[x] += 1ll * s * ss;
}
ll getsum(int x) {
ll res = 0;
for (;x;x -= (x & -x)) res += c[x];
return res;
}
ll getsum2(int x) {
ll res = 0;
for (;x;x -= (x & -x)) res += c2[x];
return res;
}
struct ask {
int op,l,r,x;
}q[300010];
int main() {
n = read(), m = read();
for (int i = 1;i <= n;i ++ ) x[i] = read(), h[++tot] = x[i];
for (int i = 1;i <= n;i ++ ) v[i] = read();
for (int i = 1;i <= m;i ++ ) {//离线离散化
q[i].op = read(), q[i].l = read(), q[i].r = read();
if (q[i].op == 2) q[i].x = read(), h[++tot] = q[i].r;
else h[++tot] = q[i].l, h[++tot] = q[i].r;
}
sort(h+1,h+tot+1);
tot = unique(h+1,h+tot+1)-h-1;
for (int i = 1;i <= n;i ++ ) {
x[i] = lower_bound(h+1,h+tot+1,x[i]) - h;
upd(x[i],v[i],h[x[i]]);
}
for (int i = 1;i <= m;i ++ ) {
if (q[i].op == 2) {
q[i].r = lower_bound(h+1,h+tot+1,q[i].r) - h;
int t = q[i].l;
upd(x[t],-v[t],h[x[t]]);//直接单点修改
x[t] = q[i].r, v[t] = q[i].x;
upd(x[t],v[t],h[x[t]]);
}
else {
q[i].r = lower_bound(h+1,h+tot+1,q[i].r) - h;//离散
q[i].l = lower_bound(h+1,h+tot+1,q[i].l) - h;
ll t = getsum(q[i].l-1),s = getsum(q[i].r) - t >> 1;
int l = q[i].l - 1;
for (int j = 1 << 19;j;j >>= 1)//倍增找到第s/2个人住的房子
l += (l + j <= q[i].r && getsum(l + j) - t <= s) * j;
l ++;
printf("%lld\n",(getsum(l) - t) * h[l] - getsum2(l) + getsum2(q[i].l-1) - (getsum(q[i].r) - getsum(l)) * h[l] + getsum2(q[i].r) - getsum2(l));
//直接套式子
}
}
return 0;
}
线段树二分好像是一个