HDU1394 Minimum Inversion Number 线段树+数学

来源:互联网 发布:淘宝7.1.0版本官方网 编辑:程序博客网 时间:2024/04/27 23:32

Problem Description

The inversion number of a given number sequence a1, a2, …, an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.

For a given sequence of numbers a1, a2, …, an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:

a1, a2, …, an-1, an (where m = 0 - the initial seqence)
a2, a3, …, an, a1 (where m = 1)
a3, a4, …, an, a1, a2 (where m = 2)

an, a1, a2, …, an-1 (where m = n-1)

You are asked to write a program to find the minimum inversion number out of the above sequences.

Input

The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.

Output

For each case, output the minimum inversion number on a single line.

Sample Input

10
1 3 6 9 0 8 5 7 4 2

Sample Output

16

解题思路

这题折腾了一晚上才AC 还是太粗心了。

因为数据小,貌似暴力也是可以过的。用线段树当然更好。
解题分为两步
①先不考虑移动,用O(nlogn)把初始的逆序对个数算出来(假设是sum)

②用数学公式用O(1)递推sum 找到最小情况的sum

先看第一步,用线段树。每读入一个数我们只考虑在它之前已经读入的数。便于统计我们将所有的s[i]++成为1-n的正整数。再看当前读入的这个数和n之间的这一段距离已经存在了几个数。他们就是当前这个数的”前瞻逆序对”

第二步。假设当前逆序对是sum个。现在考虑第一个数 假设是k 将它放到队伍的最后面。我们知道在第2到第n个数中有k-1个是小于k的,n-k个是大于k的。所以这么一移动,自然逆序对数量就少了k-1个,多了n-k个。这样不断循环,不断更新最小值。

代码

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int maxn = 5010;int n;int s[maxn];int ans[maxn];int segTree[maxn<<2];void update(int node,int k,int l,int r){    if(l <= k && k <= r) segTree[node] ++;    if(l == r) return;    if(r < k || l > k) return;    update(node<<1,k,l,(l+r)/2);    update((node<<1)+1,k,(l+r)/2+1,r);}int query(int a,int b,int node,int l,int r){    if(l > b || r < a) return 0;    if(a <= l && r <= b) return segTree[node];    return query(a,b,node<<1,l,(l+r)/2)+query(a,b,(node<<1)+1,(l+r)/2+1,r);}int main(){    while(scanf("%d",&n) != EOF) {        memset(s,0,sizeof(s));        memset(ans,0,sizeof(ans));        memset(segTree,0,sizeof(segTree));        for(int i = 1 ; i <= n ; i ++) {            scanf("%d",&s[i]);            s[i] ++;            ans[i] = query(s[i],n,1,1,n);            update(1,s[i],1,n);        }        int sum = 0;        for(int i = 1 ; i <= n ; i ++) sum += ans[i];        int mi = sum;        for(int i = 1 ; i < n ; i ++) {            sum = sum+1+n-2*s[i];            if(sum < mi) mi = sum;        }        printf("%d\n",mi);    }    return 0;}
2 0
原创粉丝点击