交换两个数,求使得排列的逆序数最多减少多少,以及有多少个这样的数对
数据范围有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<stdio.h>
#include<string.h>
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;i<n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(num[i]>num[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);
}