BZOJ 2141 排队 [分块+树状数组]

题意:给长度为n的数列,求初始逆序对和交换两个点之后的逆序对。

题解:初始逆序对直接树状数组,交换两个点之后的逆序对和这两个点之间的区间有关,用分块记录每个块中数大小的情况。当两个端点不在同一个块时,要查询前半段、中间整块和后半段的情况,当两个端点在同一个块时,直接查询u到v。(注意题目给的区间不一定是u<v)

AC代码:

#include<stdio.h>
#include<algorithm>
#include<math.h>
#define N 20200 
using namespace std;
int a[N],b[N];
int c[N],C[205][N];
int top;
int lowbit(int i)
{
	return i&(-i);
}
void add(int i)
{
	while(i<=top)
	{
		c[i]++;
		i+=lowbit(i);
	}
}
void addd(int pos,int i,int k)
{
	while(i<=top)
	{
		C[pos][i]+=k;
		i+=lowbit(i);
	}
}
int sum(int i)
{
	int ans=0;
	while(i)
	{
		ans+=c[i];
		i-=lowbit(i);
	}
	return ans;
}
int Sum(int pos,int i)
{
	int ans=0;
	while(i)
	{
		ans+=C[pos][i];
		i-=lowbit(i);
	}
	return ans;
}
int L[N],R[N];
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+n+1);
	top=unique(b+1,b+n+1)-b;
	int ans=0;
	int len=(int)sqrt(n);
	for(int i=1;i<=n;i++)
	{
		int pos=lower_bound(b+1,b+top,a[i])-b;
		ans+=sum(top)-sum(pos);
		add(pos);
		addd(i/len,pos,1);
		if(L[i/len]==0)L[i/len]=i;
		R[i/len]=i;
	}
	printf("%d\n",ans);
	int m;
	scanf("%d",&m);
	for(int i=0;i<m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		if(u>v) swap(u,v); 
		int dau,xiaou,dav,xiaov;
		dau=xiaou=dav=xiaov=0;
		int pos1=lower_bound(b+1,b+top,a[u])-b;
		int pos2=lower_bound(b+1,b+top,a[v])-b;
		int b1=u/len,b2=v/len;
		if(b1!=b2)
		{
			for(int j=u+1;j<=R[b1];j++)
			{
				if(a[u]>a[j])xiaou++;
				if(a[u]<a[j])dau++;
				if(a[v]>a[j])xiaov++;
				if(a[v]<a[j])dav++;
			}
			for(int j=b1+1;j<=b2-1;j++)
			{
				dau+=Sum(j,top-1)-Sum(j,pos1);
				xiaou+=Sum(j,pos1-1);
				dav+=Sum(j,top-1)-Sum(j,pos2);
				xiaov+=Sum(j,pos2-1);
			}
			for(int j=L[b2];j<v;j++)
			{
				if(a[u]>a[j])xiaou++;
				if(a[u]<a[j])dau++;
				if(a[v]>a[j])xiaov++;
				if(a[v]<a[j])dav++;
			}
		}
		else 
		{
			for(int j=u+1;j<v;j++)
			{
				if(a[u]>a[j])xiaou++;
				if(a[u]<a[j])dau++;
				if(a[v]>a[j])xiaov++;
				if(a[v]<a[j])dav++;
			}
		}
		ans+=dau-xiaou+xiaov-dav;
		if(a[u]>a[v])ans--;
		else if(a[u]<a[v])ans++;
		printf("%d\n",ans);
		addd(b1,pos1,-1);
		addd(b1,pos2,1);
		addd(b2,pos2,-1);
		addd(b2,pos1,1);
		swap(a[u],a[v]);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值