题意:
现在有n个物品,第i个物品他的位置在a[i],他的重量为w[i]。每一个物品移动一步的代价为他的w[i]。目前有2种操作:
1. x y 将第x的物品的重量改为y
2.l r 将编号在 [ l, r ]之间的所有物品移动到一起,求最小的花费是多少。
带权中位数,学习了->这里
先来考虑一下货仓选址问题,就是一堆不带权值的数,选出一个点使得所有点到他的距离之和最小,那么肯定是选中位数最优
然后加上权值限制,这玩意儿有个学名叫做带权中位数
带权中位数为满足$sum_{i=1}^x w_igeq sum_{i=x+1}^n w_i$的最大的$x$
简单证(kou)明(hu)一下为什么是对的,假设将区间内的所有数移到$x+1$的位置,那么$[1,x]$内的数全都要多走$dis[x,x+1]$的路程,而$[x+1,n]$内的数可以少走这么多路程,那么总权值的变化量就为$dis[x,x+1]*(sum_{i=1}^x w_i-sum_{i=x+1}^n w_i)$,可以发现若上面那个不等式不成立则往右移总权值变小,也就是说答案会更优,所以只有当上面那个等式成立时才不会往右移更优。又因为这玩意儿是从左边移过来的,所以左边的都比他劣。那么它一定是最优的。
于是这个东西我们可以二分
然后我们来考虑一下原问题,发现所有的移动方案都可以等价于固定一个点不动,其他的点向它靠。那么我们设$pos$为最优的点,然后用和上面的方法一样考虑可以发现当$pos$为带权中位数时答案最优。所以我们可以直接二分出$pos$
现在的问题就是如何快速计算总代价了,即$sum_{i=l}^{pos-1}w_i*(a_{pos}-a_i-(pos-i))+sum_{i=pos+1}^{r}w_i*(a_{i}-a_{pos}-(i-pos))$
考虑左边这一坨(右边同理),化一下式子可得它等于$$sum_{i=l}^{pos-1}w_i*(a_{pos}-pos-(a_i-i))$$
然后右边也是差不多的形式,于是只要维护$sum w_i$和$sum w_i*(a_i-i)$就能快速算出答案了
于是用树状数组维护这两个玩意儿,然后二分找出带权中位数,每一次快速计算答案即可
1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #define ll long long 6 using namespace std; 7 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 8 char buf[1<<21],*p1=buf,*p2=buf; 9 inline int read(){ 10 #define num ch-'0' 11 char ch;bool flag=0;int res; 12 while(!isdigit(ch=getc())) 13 (ch=='-')&&(flag=true); 14 for(res=num;isdigit(ch=getc());res=res*10+num); 15 (flag)&&(res=-res); 16 #undef num 17 return res; 18 } 19 char sr[1<<21],z[20];int C=-1,Z; 20 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} 21 inline void print(int x){ 22 if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x; 23 while(z[++Z]=x%10+48,x/=10); 24 while(sr[++C]=z[Z],--Z);sr[++C]=' '; 25 } 26 const int N=2e5+5;const ll P=1e9+7; 27 int n,q;ll a[N],w[N]; 28 namespace SUM{ 29 ll c[N]; 30 inline void add(int x,ll y){ 31 for(;x<=n;x+=x&-x) c[x]+=y; 32 } 33 inline ll que(int x){ 34 ll res=0; 35 for(;x;x-=x&-x) res+=c[x]; 36 return res; 37 } 38 inline ll query(int l,int r){ 39 if(r<l) return 0; 40 return que(r)-que(l-1); 41 } 42 } 43 namespace MUL{ 44 ll c[N]; 45 inline void add(int x,ll y){ 46 y%=P; 47 for(;x<=n;x+=x&-x) 48 (c[x]+=y+P)%=P; 49 } 50 inline ll que(int x){ 51 ll res=0; 52 for(;x;x-=x&-x) (res+=c[x])%=P; 53 return res; 54 } 55 inline ll query(int l,int r){ 56 if(r<l) return 0; 57 return (que(r)-que(l-1)+P)%P; 58 } 59 } 60 inline int getpos(int ql,int qr){ 61 int l=ql,r=qr,mid,res; 62 while(l<=r){ 63 mid=(l+r)>>1; 64 if(SUM::query(ql,mid)>=SUM::query(mid+1,qr)) res=mid,r=mid-1; 65 else l=mid+1; 66 } 67 return res; 68 } 69 void solve(int l,int r){ 70 if(l==r) return (void)(print(0)); 71 int pos=getpos(l,r);ll res=0; 72 res=(-MUL::query(l,pos)+(SUM::query(l,pos) % P)*(ll)abs(a[pos] - pos)%P+P)%P; 73 res=(res+MUL::query(pos,r)-SUM::query(pos,r)%P*(a[pos]-pos)%P+P)%P; 74 print(res); 75 } 76 int main(){ 77 // freopen("testdata.in","r",stdin); 78 n=read(),q=read(); 79 for(int i=1;i<=n;++i) a[i]=read(); 80 for(int i=1;i<=n;++i){ 81 w[i]=read(),SUM::add(i,w[i]),MUL::add(i,w[i]*(a[i]-i)); 82 } 83 while(q--){ 84 int x=read(),y=read(); 85 if(x<0){ 86 x=-x,SUM::add(x,-w[x]),MUL::add(x,-w[x]*(a[x]-x)); 87 w[x]=y,SUM::add(x,y),MUL::add(x,y*(a[x]-x)); 88 }else solve(x,y); 89 } 90 Ot(); 91 return 0; 92 }