线段数 --- (单点更新、求逆序对)

Minimum Inversion Number

Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

 

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
 

用线段树求逆序对的水题,维护的是区间内整数的个数,每次插入 x 时,查询在 x 的前面比小的数的个数,并计算出比 x 大的数的个数 cnt[x] ,最后将 cnt 累加,即为逆序数;

将队首的数放到队尾后逆序数改变:n-1-a[i], a[i] 为原始序列中第 i 个数的值;

# include <stdio.h>
# include <string.h>
# define N 50010

int n, D;
int sum[4 * N], cnt[N], a[N];

void update(int i)
{
    for (; i ^ 1; i >>= 1)
    {
        sum[i >> 1] = sum[i] + sum[i ^ 1];
    }
}

int query(int s, int t)
{
    int ret;
    
    ret = 0;
    s += D-1, t += D+1;
    for ( ; s ^ t ^ 1; s >>= 1, t >>= 1)
    {
        if (~s & 0x1) ret += sum[s+1];
        if ( t & 0x1) ret += sum[t-1];
    }

    return ret;
}

void init(void)
{
    int i;
    
    for (D = 1; D < n+2; D <<= 1) ;
    
    memset(sum, 0, sizeof(sum[0])*2*D+5);
    memset(cnt, 0, sizeof(cnt[0])*n+5);
    memset(a, 0, sizeof(a[0])*n+5);
    
    for (i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i]);
        cnt[i] = i - 1 - query(0, a[i]);
        ++sum[a[i]+D], update(a[i]+D);
    }
}

void solve(void)
{
    int rev, i, min;

    rev = 0;
    for (i = 1; i <= n; ++i)
    {
        rev += cnt[i];
    }
    min = rev;
    for (i = 1; i <= n; ++i)
    {
        rev += n-1-2*a[i];
        if (min > rev) min = rev;
    }
    printf("%d
", min);
}

int main()
{    
    while (~scanf("%d", &n))
    {
        init();
        solve();
    }

    return 0;
}

树状数组实现:

#include<stdio.h>
#include<string.h>
#define min(a,b)(a)<(b)?(a):(b);
int v[5002];
int l[5002];
int n;
int lowbit(int x)
{
    return (x)&(-x);
}
void update(int pos)
{
    while(pos<=n)
    {
        l[pos]+=1;
        pos+=lowbit(pos);
    }
}
int getsum(int x)
{
    int sum=0;
    while(x>0)
    {
        sum+=l[x];
        x-=lowbit(x);
    }
    return sum;
}
int main()
{
    int res,p,i,sum;
    while(scanf("%d",&n)!=EOF)
    {
        sum=0;
        memset(l,0,sizeof(l));
        for(i=0;i<n;i++)
        {
            scanf("%d",&v[i]);
            v[i]++;
            sum+=(getsum(n)-getsum(v[i]));
            update(v[i]);
        }
        res=sum;
        for(i=0;i<n;i++)
        {
            sum+=n-v[i]-v[i]+1;
            res=min(res,sum);
        }
        printf("%d
",res);
    }
    return 0;
}

归并排序实现:

#include <stdio.h>
long long left[100001], right[100001];
long long count;
void merge(long long* a, long long p, long long q, long long r)
{
    long long i, j, k, n1, n2;
    n1 = q-p+1;
    n2 = r-q;
    for (i=0; i<n1; ++i)
    {
        left[i] = a[p+i];
    }
    for (i=0; i<n2; ++i)
    {
        right[i] = a[q+i+1];
    }
    left[n1] = right[n2] =2147483647;

    i = j = 0;
    for (k=p; k<=r; ++k)
    {
        if (left[i] <= right[j])
        {
            a[k] = left[i];
            i++;
        }
        else
        {
            a[k] = right[j];
            j++;
            count += n1-i;
        }
    }
    return;
}

void mergesort(long long* a, long long p, long long r)
{
    long long q;
    if (p < r)
    {
        q = (p+r)/2;
        mergesort(a, p, q);
        mergesort(a, q+1, r);
        merge(a, p, q, r);
    }
    return ;
}

long long n,i, t, a[100001];
int main()
{
    while (scanf("%I64d", &n)!=EOF)
    {
        count = 0;
        for (i=0; i<n; ++i)
        {
            scanf("%I64d", &a[i]);
        }
        mergesort(a, 0, n-1);
        printf("%I64d
", count);
    }
}

对比了几种求逆序对的方法后,发现还是归并好用,既快代码又短.

 

原文地址:https://www.cnblogs.com/crazyacking/p/3705415.html