codeforces 1053C Putting Boxes Together(树状数组)
题意:
给你一个数组,代表第个元素放在第个箱子里面。并且,对于第i个元素,从一个箱子移动到相邻的箱子需要花费点精力。现在有两个操作:
1.把第i个元素移动的花费变为x
2.查询[L,R]区间内的元素按原有元素顺序移动到任意连续区间[x,x+(r-l)]的最小花费。
题解:
我们看一下移动到[x,x+(r-l)]的花费:
有了这个转化之后我们可以发现这明显的就是带权中位数嘛。也就是说我们只要维护区间中位数就行了,而区间中位数可以由的区间和求出来,那么用树状数组来维护吗,然后再二分就行了。同时,也要用一个树状数组来维护区间的值,这样这题就做完啦。
#include<bits/stdc++.h>
const int mod=1e9+7;
const int maxn=2e5+7;
int n,q,a[maxn],w[maxn];
typedef long long ll;
struct Fenwick1
{
ll bs[maxn];
void add(int x,ll y)
{
for(;x<=n;x+=x&-x) bs[x]+=y;
}
ll sum(int x)
{
ll s=0;
for(;x>=1;x-=x&-x) s+=bs[x];
return s;
}
}B0;
struct Fenwick
{
int bs[maxn];
void add(int x,int y)
{
for(;x<=n;x+=x&-x) (bs[x]+=y)%=mod;
}
ll sum(int x)
{
int s=0;
for(;x>=1;x-=x&-x) (s+=bs[x])%=mod;
return s;
}
}B1;
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)
scanf("%d",a+i),a[i]-=i;
for(int i=1;i<=n;++i) scanf("%d",w+i);
for(int i=1;i<=n;++i)
B0.add(i,w[i]),
B1.add(i,w[i]*1ll*a[i]%mod);
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
if(x<0)
{
x=-x; int dt=y-w[x];
w[x]=y; B0.add(x,dt);
B1.add(x,dt*1ll*a[x]%mod);
}
else
{
ll sl=B0.sum(x-1),
su=B0.sum(y)-sl;
su=(su+1)/2;
int l=x,r=y;
while(l<r)
{
int m=(l+r)>>1;
if(B0.sum(m)-sl>=su) r=m;
else l=m+1;
}
int m=l;
//mid pos is m
ll ans=
(B1.sum(y)-B1.sum(m))-(B0.sum(y)-B0.sum(m))%mod*1ll*(a[m])%mod
-(B1.sum(m)-B1.sum(x-1))+(B0.sum(m)-B0.sum(x-1))%mod*(a[m])%mod;
ans=(ans%mod+mod)%mod;
printf("%d\n",int(ans));
}
}
}
Q.E.D.