P6309 人间之理(线段树)
前置结论:给出n个点,选择一个点p使得这个n个点到p的距离之和最小。
那么这个p一定是中间两个点的线段或中间一个点。
基于这个结论,考虑用线段树维护区间点权和和区间点权乘坐标的和。
然后对每个区间询问,二分出p的位置,对p之前和p之后的区间分类计算。
修改操作就是毒瘤离散化+模拟。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
const int M=maxn*4;
long long vx[M],v[M];
//维护每个下标的人数和人数乘坐标
int t[maxn];//离散化下标
long long vt[maxn];//第i个下标里有多少人
int b[maxn];//第i座房屋属于t里的哪个下标
long long bv[maxn];//第i座房屋有多少人
struct qnode {
int op,l,r,a,b,c;
}q[maxn];//问
int n,m;
void build (int i,int l,int r) {
if (l==r) {
v[i]=vt[l];
vx[i]=1ll*vt[l]*t[l];
return;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
v[i]=v[i<<1]+v[i<<1|1];
vx[i]=vx[i<<1]+vx[i<<1|1];
}
void up (int i,int l,int r,int x,int y) {
if (l==x&&r==x) {
v[i]+=y;
vx[i]+=1ll*y*t[l];
return;
}
int mid=(l+r)>>1;
if (x<=mid) up(i<<1,l,mid,x,y);
if (x>mid) up(i<<1|1,mid+1,r,x,y);
v[i]=v[i<<1]+v[i<<1|1];
vx[i]=vx[i<<1]+vx[i<<1|1];
}
long long queryv (int i,int l,int r,int L,int R) {
if (l>=L&&r<=R) return v[i];
int mid=(l+r)>>1;
long long ans=0;
if (L<=mid) ans+=queryv(i<<1,l,mid,L,R);
if (R>mid) ans+=queryv(i<<1|1,mid+1,r,L,R);
return ans;
}
long long queryvx (int i,int l,int r,int L,int R) {
if (l>=L&&r<=R) return vx[i];
int mid=(l+r)>>1;
long long ans=0;
if (L<=mid) ans+=queryvx(i<<1,l,mid,L,R);
if (R>mid) ans+=queryvx(i<<1|1,mid+1,r,L,R);
return ans;
}
int main () {
int tot=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) {
scanf("%d",b+i);
t[++tot]=b[i];
}
for (int i=1;i<=n;i++) {
scanf("%d",bv+i);
}
for (int i=1;i<=m;i++) {
scanf("%d",&q[i].op);
if (q[i].op==1) {
scanf("%d%d",&q[i].l,&q[i].r);
t[++tot]=q[i].l;
t[++tot]=q[i].r;
}
else {
scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].c);
t[++tot]=q[i].b;
}
}
sort(t+1,t+tot+1);
int mm=unique(t+1,t+tot+1)-t-1;
for (int i=1;i<=n;i++) {
b[i]=upper_bound(t+1,t+mm+1,b[i])-t-1;
vt[b[i]]+=bv[i];
}
build(1,1,mm);//对每个下标建立线段树
for (int i=1;i<=m;i++) {
if (q[i].op==1) {
q[i].l=upper_bound(t+1,t+mm+1,q[i].l)-t-1;
q[i].r=upper_bound(t+1,t+mm+1,q[i].r)-t-1;
long long sumv=queryv(1,1,mm,q[i].l,q[i].r);//先查出q[i].l到q[i].r里有多少人
sumv=sumv/2+sumv%2;
int pp=-1,l=q[i].l,r=q[i].r;
while (l<=r) {
int mid=(l+r)>>1;
if (queryv(1,1,mm,q[i].l,mid)>=sumv) {
pp=mid;
r=mid-1;
}
else {
l=mid+1;
}
}
//printf("%d %d %d\n",pp,q[i].l,q[i].r);
//对pp前面的所有坐标,vp-vx,就是点权和*p-vx和
long long ans=0;
ans+=queryv(1,1,mm,q[i].l,pp)*t[pp]-queryvx(1,1,mm,q[i].l,pp);
//对pp后面的坐标,就是vx和-点权和*p
ans-=queryv(1,1,mm,pp+1,q[i].r)*t[pp]-queryvx(1,1,mm,pp+1,q[i].r);
printf("%lld\n",ans);
}
else {
up(1,1,mm,b[q[i].a],-bv[q[i].a]);
q[i].b=upper_bound(t+1,t+mm+1,q[i].b)-t-1;
bv[q[i].a]=q[i].c;
b[q[i].a]=q[i].b;
//printf("%d\n",b[q[i].a]);
up(1,1,mm,b[q[i].a],bv[q[i].a]);
}
}
}