Petya is a beginner programmer. He has already mastered the basics of the C++ language and moved on to learning algorithms. The first algorithm he encountered was insertion sort. Petya has already written the code that implements this algorithm and sorts the given integer zero-indexed array a of size n in the non-decreasing order.
for (int i = 1; i < n; i = i + 1)
{
int j = i;
while (j > 0 && a[j] < a[j - 1])
{
swap(a[j], a[j - 1]); // swap elements a[j] and a[j - 1]
j = j - 1;
}
}
Petya uses this algorithm only for sorting of arrays that are permutations of numbers from 0 to n - 1. He has already chosen the permutation he wants to sort but he first decided to swap some two of its elements. Petya wants to choose these elements in such a way that the number of times the sorting executes function swap, was minimum. Help Petya find out the number of ways in which he can make the swap and fulfill this requirement.
It is guaranteed that it's always possible to swap two elements of the input permutation in such a way that the number of swap function calls decreases.
The first line contains a single integer n (2 ≤ n ≤ 5000) — the length of the permutation. The second line contains n different integers from 0 to n - 1, inclusive — the actual permutation.
Print two integers: the minimum number of times the swap function is executed and the number of such pairs (i, j) that swapping the elements of the input permutation with indexes i and j leads to the minimum number of the executions.
5
4 0 3 1 2
3 2
5
1 2 3 4 0
3 4
In the first sample the appropriate pairs are (0, 3) and (0, 4).
In the second sample the appropriate pairs are (0, 4), (1, 4), (2, 4) and (3, 4).
题意:给出n个数,范围0到n-1,然后给出一个插入排序的代码,可以交换两个数,然后使交换后的排列使用交换函数的次数最少,然后问交换后的排列的调用交换函数最少次数是多少
和能有几个索引对交换可以的到最少
思路:首先它的范围只有5000,然后我们看到那个插入排序想想,他只有前面大于后面的就要交换一次,所以没交换的时候的次数其实就是整个排列的逆序数,然后我们其实考虑的就是
交换一个索引使得他的逆序对个数减少最多,然后求有几个这样的索引即可,逆序数这部分我们可以使用树状数组求,我们枚举每个索引,只有前面这个数大于后面的时候我们才要考虑
是否要交换
给出一个例子
4 5 0 2 3 7 1
然后我们当前访问的索引对是(0, 7)-〉4 1
然后我们如何计算4 1交换后能减少多少个逆序对呢
首先我们如果交换 4 1的话 中间那些比4大的数的逆序对肯定不会改变,因为这些数都是大于4的,我把1 4交换,4还是小于那些数,这些逆序对就不会改变了
小于1的数也不用考虑,因为例子中的4 1交换,中间那个0还是小于1,逆序对还是存在,所以并 没有改变
所以我们只要考虑那些小于4大于1的数(a[j]<x<a[i])
我们用树状数组来优化处理
把中间小于4的数都入树状数组
然后是1的时候,我们找出大于1的数有多少然后我们可以找到2 3这两个数
然后我们把4 和1交换之后,中间那些小于4大于1的数因为大于他们的4到了后面,他们的逆序对都-1,还有1移到了2 3这些数前面,所以又要减去这么多逆序对数
总的就是 a[j]<x<a[i] 的两倍
#include<cstdio> #include<cstring> using namespace std; int n,a[5001]; int f[5001]; int lowbit(int x) { return x&(-x); } int add(int x) { while(x) { f[x]++; x-=lowbit(x); } } int query(int x) { int sum=0; while(x<=n) { sum+=f[x]; x+=lowbit(x); } return sum; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); a[i]++; } int s=0; int mx=0,cs=0; for(int i=1;i<=n;i++) { memset(f,0,sizeof(f)); //把之前树状数组的数清空 for(int j=i+1;j<=n;j++) { if(a[i]>a[j]) { s++;//存的是总的逆序对个数 int num=1+2*query(a[j]);//求得能减去的逆序对个数,+1是因为当前还没有入树状数组 if(num>mx)//大于更新 { mx=num; cs=1; } else if(num==mx) //等于得时候说明索引对又新加了一对 { cs++; } add(a[j]); } } } printf("%d %d",s-mx,cs); }