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]);
		}
	} 
} 
posted @ 2021-07-21 20:22  zlc0405  阅读(35)  评论(0编辑  收藏  举报