P6309 人間之理(線段樹)
阿新 • • 發佈:2021-07-21
前置結論:給出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]); } } }