codeforces 1053C Putting Boxes Together(树状数组)

传送门

题意:

给你一个数组aaaia_i代表第ii个元素放在第aia_i个箱子里面。并且,对于第i个元素,从一个箱子移动到相邻的箱子需要花费wiw_i点精力。现在有两个操作:
1.把第i个元素移动的花费变为x
2.查询[L,R]区间内的元素按原有元素顺序移动到任意连续区间[x,x+(r-l)]的最小花费。

题解:

我们看一下移动到[x,x+(r-l)]的花费:

cost=i=lrwiaxai+ix=i=lrwiaxx(aii)=i=lrwibxbicost = \sum_{i=l}^{r}{w_{i}*\left|a_x-a_i+i-x\right|} \newline= \sum_{i=l}^{r}{w_{i}*\left|a_x-x-(a_i-i)\right|} \newline= \sum_{i=l}^{r}{w_{i}*\left|b_x-b_i\right|}

有了这个转化之后我们可以发现这明显的就是带权中位数嘛。也就是说我们只要维护区间中位数就行了,而区间中位数可以由ww的区间和求出来,那么用树状数组来维护吗,然后再二分就行了。同时,也要用一个树状数组来维护区间wiaiw_i*a_i的值,这样这题就做完啦。

#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.