BZOJ2141 排队(分块)

【题解】

分块,每块内部从小到大排序,每次询问,在完整的块内二分查找大于或小于 移动的值 的数的个数 

复杂度:
初始求逆序对 O( n*sqrt(n) )

询问 O( m*sqrt(n) )


【代码】

#include<stdio.h>
#include<stdlib.h>
#define SIZE 128
int h[20005],a[20005],block[20005],L[200],R[200];
void jh(int* a,int* b)
{
	int t=*a;
	*a=*b;
	*b=t;
}
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]);
			i++;
			j--;
		}
	}
	if(j>low) kp(low,j);
	if(i<high) kp(i,high);
}
int find_down(int num,int left,int right)
{
	int mid;
	if(a[left]>=num) return left-1;
	while(left<right)
	{
		mid=(left+right+1)/2;
		if(a[mid]>=num) right=mid-1;
		else left=mid;
	}
	return left;
}
int find_up(int num,int left,int right)
{
	int mid;
	if(a[right]<=num) return right+1;
	while(left<right)
	{
		mid=(left+right)/2;
		if(a[mid]<=num) left=mid+1;
		else right=mid;
	}
	return left;
}
int cx_down(int num,int x,int y)
{
	int i,ans=0;
	if(block[x]+1>=block[y])
	{
		for(i=x;i<=y;i++)
			if(h[i]<num) ans++;
		return ans;
	}
	for(i=x;i<=R[block[x]];i++)
		if(h[i]<num) ans++;
	for(i=L[block[y]];i<=y;i++)
		if(h[i]<num) ans++;
	for(i=block[x]+1;i<block[y];i++)
		ans+=find_down(num,L[i],R[i])-L[i]+1;
	return ans;
}
int cx_up(int num,int x,int y)
{
	int i,ans=0;
	if(block[x]+1>=block[y])
	{
		for(i=x;i<=y;i++)
			if(h[i]>num) ans++;
		return ans;
	}
	for(i=x;i<=R[block[x]];i++)
		if(h[i]>num) ans++;
	for(i=L[block[y]];i<=y;i++)
		if(h[i]>num) ans++;
	for(i=block[x]+1;i<block[y];i++)
		ans+=R[i]-find_up(num,L[i],R[i])+1;
	return ans;
}
int main()
{
	int n,m,i,x,y,cnt,ans=0;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&h[i]);
		a[i]=h[i];
		block[i]=(i-1)/SIZE+1;
	}
	cnt=(n-1)/SIZE+1;
	for(i=1;i<=cnt;i++)
	{
		L[i]=(i-1)*SIZE+1;
		R[i]=i*SIZE;
	}
	R[cnt]=n;
	for(i=1;i<=cnt;i++)
		kp(L[i],R[i]);
	for(i=1;i<=n;i++)
		ans+=cx_up(h[i],1,i-1);
	printf("%d\n",ans);
	scanf("%d",&m);
	for(;m>0;m--)
	{
		scanf("%d%d",&x,&y);
		if(x>y) jh(&x,&y);
		if(h[x]<h[y]) ans++;
		if(h[x]>h[y]) ans--;
		if(x+1<y)
		{
			ans=ans+cx_down(h[y],x+1,y-1)-cx_up(h[y],x+1,y-1);
			ans=ans-cx_down(h[x],x+1,y-1)+cx_up(h[x],x+1,y-1);
		}
		jh(&h[x],&h[y]);
		for(i=L[block[x]];i<=R[block[x]];i++)
			a[i]=h[i];
		kp(L[block[x]],R[block[x]]);
		for(i=L[block[y]];i<=R[block[y]];i++)
			a[i]=h[i];
		kp(L[block[y]],R[block[y]]);
		printf("%d\n",ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值