洛谷CF1030F Putting Boxes Together(树状数组)

题意:

现在有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 }
原文地址:https://www.cnblogs.com/bztMinamoto/p/9764334.html