首页 > 代码库 > BZOJ 2141 排队 分块+树状数组

BZOJ 2141 排队 分块+树状数组

题目大意:给定一个序列,m次交换两个数,求初始逆序对数及每次交换后的逆序对数

首先离散化,分块,对于每块建立一个树状数组,保存这个块中的所有元素

然后对于每个询问(x,y) (x<y) 两侧的数是没有影响的,区间(x,y)的数a[i]讨论如下:

a[i]<a[x] --ans

a[i]>a[x] ++ans

a[i]<a[y] ++ans

a[i]>a[y] --ans

然后对于块中的树状数组处理,块外的暴力

注意此题元素有重复 亲测可信

RANK5吓尿0.0 为何块套树要比树套树还快……


#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 20200
using namespace std;
int n,m,ans,tot,block,a[M];
pair<int,int>b[M];
int pre[M],cnt[200][M];
void Update(int c[],int x)
{
	for(;x<=n;x+=x&-x)
		++c[x];
}
void Downdate(int c[],int x)
{
	for(;x<=n;x+=x&-x)
		--c[x];
}
int Get_Ans(int c[],int x)
{
	int re=0;
	for(;x;x-=x&-x)
		re+=c[x];
	return re;
}
int main()
{
	int i,j,x,y;
	cin>>n;
	for(i=1;i<=n;i++)
		scanf("%d",&b[i].first),b[i].second=i;
	sort(b+1,b+n+1);
	for(i=1;i<=n;i++)
	{
		if(b[i].first!=b[i-1].first)
			++tot;
		a[b[i].second]=tot;
	}
	for(i=n;i;i--)
		ans+=Get_Ans(pre,a[i]-1),Update(pre,a[i]);
	block=static_cast<int>(sqrt(n)+1e-7);
	for(i=1;i<=n;i++)
		Update(cnt[(i-1)/block],a[i]);
	cout<<ans<<endl;
	cin>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		if(x>y) swap(x,y);
		//查询(x,y)区间内有多少个数比a[x]和a[y]小
		int b1=(x-1)/block+1;
		int b2=(y-1)/block-1;
		if(b1<=b2)
		{
			for(j=b1;j<=b2;j++)
			{
				ans-=Get_Ans(cnt[j],a[x]-1);
				ans+=Get_Ans(cnt[j],n)-Get_Ans(cnt[j],a[x]);
				ans+=Get_Ans(cnt[j],a[y]-1);
				ans-=Get_Ans(cnt[j],n)-Get_Ans(cnt[j],a[y]);
			}
			for(j=x+1;j<=b1*block;j++)
			{
				if(a[j]<a[x]) --ans;
				if(a[j]>a[x]) ++ans;
				if(a[j]<a[y]) ++ans;
				if(a[j]>a[y]) --ans;
			}
			for(j=(b2+1)*block+1;j<y;j++)
			{
				if(a[j]<a[x]) --ans;
				if(a[j]>a[x]) ++ans;
				if(a[j]<a[y]) ++ans;
				if(a[j]>a[y]) --ans;
			}
		}
		else
		{
			for(j=x+1;j<=y-1;j++)
			{
				if(a[j]<a[x]) --ans;
				if(a[j]>a[x]) ++ans;
				if(a[j]<a[y]) ++ans;
				if(a[j]>a[y]) --ans;
			}
		}
		if(a[x]<a[y]) ++ans;
		else if(a[x]>a[y]) --ans;
		printf("%d\n",ans);
		Downdate(cnt[(x-1)/block],a[x]);
		Downdate(cnt[(y-1)/block],a[y]);
		swap(a[x],a[y]);
		Update(cnt[(x-1)/block],a[x]);
		Update(cnt[(y-1)/block],a[y]);
	}
}


BZOJ 2141 排队 分块+树状数组