• 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
    ",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
    ",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
    ",b[q[i].a]);
    			up(1,1,mm,b[q[i].a],bv[q[i].a]);
    		}
    	} 
    }
  • 相关阅读:
    python的xpinyin模块:汉字转拼音
    中文报错SyntaxError: Non-UTF-8 code starting with 'xe6' in file
    "底层逻辑”是什么意思?
    【Golang】关于Go中logrus的用法
    【Golang】 关于Go语言中的锁
    【Golang】Go 通过结构(struct) 实现接口(interface)
    【Golang】Go语言之log的使用
    【Golang】Go中时间(time)的用法以及gorm处理时间戳
    【Golang】Go中三个点(...)用法
    一文彻底掌握Apache Hudi异步Clustering部署
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15041452.html
Copyright © 2020-2023  润新知