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

来源:互联网 发布:windows api 怎么使用 编辑:程序博客网 时间:2024/06/07 13:18

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

数据范围有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);}


原创粉丝点击