BZOJ3192 [JLOI2013]删除物品(树状数组)

【题解】

将两堆物品拼接到一起,物品的移动次数等价于中间的"断点"的移动距离之和 
通过排序预处理出每次删除后的下一个该删除的位置 
每个物品代表一条长度为1的线段,该物品删除后,线段长度改为0 
然后两点之间的距离就转化为了区间和,用树状数组维护即可 


【代码】

#include<stdio.h>
#include<stdlib.h>
typedef long long LL;
int a[100005],b[100005],c[100005];
int n;
void jh(int* a,int* b)
{
	int t=*a;
	*a=*b;
	*b=t;
}
void xg(int p,int i)
{
	for(;i<=n;i+=i&(-i))
		c[i]+=p;
}
LL cx(int i)
{
	int ans=0;
	for(;i>0;i-=i&(-i))
		ans+=c[i];
	return (LL)ans;
}
void kp(int low,int high)
{
	int i=low,j=high,mid=a[(i+j)/2];
	while(i<j)
	{
		while(a[i]>mid) i++;
		while(a[j]<mid) j--;
		if(i<=j)
		{
			jh(&a[i],&a[j]);
			jh(&b[i],&b[j]);
			i++;
			j--;
		}
	}
	if(j>low) kp(low,j);
	if(i<high) kp(i,high);
}
int main()
{
	LL ans=0;
	int n1,n2,i,p;//p:当前断点在第几个元素之前 
	scanf("%d%d",&n1,&n2);
	for(i=n1;i>=1;i--)
		scanf("%d",&a[i]);
	for(i=n1+1;i<=n1+n2;i++)
		scanf("%d",&a[i]);
	p=n1+1;
	n=n1+n2;
	for(i=1;i<=n;i++)
	{
		b[i]=i;
		xg(1,i);
	}
	kp(1,n);
	for(i=1;i<=n;i++)
	{
		if(p>b[i])
		{
			ans+=cx(p-1)-cx(b[i]);
			p=b[i]+1;
		}
		else
		{
			ans+=cx(b[i]-1)-cx(p-1);
			p=b[i];
		}
		xg(-1,b[i]);
	}
	printf("%lld",ans);
	return 0;
}


你可能感兴趣的:(树状数组)