1. 程式人生 > 其它 >P6309 人間之理(線段樹)

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]);
		}
	} 
}