CF 362C 冒泡排序 交换哪两个数逆序数减少最多

交换两个数,求使得排列的逆序数最多减少多少,以及有多少个这样的数对

数据范围有5000,我的算法是n^2logn

因为有n^2个询问,所以最好O(1)的回答,所以需要先预处理一下。

怎样快速回答交换两个数之后逆序数的改变呢?

这就要快速的算出i和j之间有多少个比num[i]小的数,有多少个比num[j]小的数,比它们大的数一减就OK。

设 si为i到j中比i小的数 bi为比i大的数 sj为比j小的数,bj为比j大的数。

所以逆序数减少量为旧逆序数-新逆序数=si-bi+bj-sj-1 最后那个1是指i和j交换后减少的逆序数。

#include
#include


int res[5200][5200];//到i为止小于num[j]的数字
int bit[5200],n;
int num[5200];

int sum(int i)
{
	int s=0;
	while(i>0)
	{
		s+=bit[i];
		i -= i & -i;
	}
	return s;
}

void add(int i,int x)
{
	while(i<=n)
	{
		bit[i]+=x;
		i += i & -i;
	}
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&num[i]);
		num[i]++;
	}
	memset(bit,0,sizeof(bit));
	memset(res,0,sizeof(res));
	int tot=0;
	for(int i=1;i<=n;i++)
	{
		add(num[i],1);
		for(int j=1;j<=n;j++)
		{
			if(num[j]==0) continue;
			res[i][j]=sum(num[j]-1);
		}
		if(num[i]==0) continue;
		tot+=(i-sum(num[i]));
	}
	
	int max_=-1;
	int ans=0;
	for(int i=1;inum[j])
			{
				int tn=j-i-1;
				int si=res[j][i]-res[i][i];
				int bi=tn-si;
				int sj=res[j][j]-res[i][j];
				int bj=tn-sj;
				int t=si-bi+bj-sj-1;
				if(t>max_)
				{
					max_=t;
					ans=1;
				}else if(t==max_)
				{
					ans++;
				}
			}
		}
	}	
	printf("%d %d\n",tot-max_,ans);
}


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