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;
}


發佈了110 篇原創文章 · 獲贊 8 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章