HDU 1394 & ZOJ 1484 Minimum Inversion Number

来源:互联网 发布:crossfit 知乎 编辑:程序博客网 时间:2024/05/07 10:20

(更新点查询区间)

这题重在想到,写代码很容易了。。这题是利用线段树求逆序数,不按给定的顺序建树,而是有序地插入。比如每插入一个数,就统计之前插入的那些数里比他大的有多少个,这个数就是此时的逆序数,然后累加每个的逆序数,就是整个原始序列的逆序数,怎么统计呢?前面说了,是有序的插入,查询比它大的数岂不是查它右边就好了?即查询a[i]~n-1中插入了多少数,凡插入了的即是比他大的。这样,总的逆序数就出来了。现在求一个最小值。这里有个结论,因为每次都把第一个移到最后即可,考虑第一个元素,x[0]是此时的逆序数,把x[0]放到最后,这时要增加原来比a[0]大的个数(即n-a[0]-1个)个逆序数,同时要减少a[0]个逆序数,因为放到后面去了,前面的那些比它小的数(a[0]个)不再构成逆序数。这是即 sum = sum + n - a[i] - 1 - a[i] ,跑一边循环求最小值。。感觉自己也没说清楚,还是不懂的自己体会一下吧。

 

代码:

#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>#include <utility>#include <cstdlib>using namespace std;#define N 5010int tree[4*N];int a[N];void build(int l,int r,int rt){    tree[rt] = 0;    if(l == r)    {        return;    }    int mid = (l+r)/2;    build(l,mid,2*rt);    build(mid+1,r,2*rt+1);}void update(int l,int r,int pos,int rt){    if(l == r)    {        tree[rt]++;        return;    }    int mid = (l+r)/2;    if(pos<=mid)        update(l,mid,pos,2*rt);    else        update(mid+1,r,pos,2*rt+1);    tree[rt] = tree[2*rt]+tree[2*rt+1];}int query(int l,int r,int aa,int bb,int rt){    if(aa>r||bb<l)        return 0;    if(aa<=l&&bb>=r)        return tree[rt];    int mid = (l+r)/2;    int res = 0;    if(aa<=mid)        res += query(l,mid,aa,bb,2*rt);    if(bb>mid)        res += query(mid+1,r,aa,bb,2*rt+1);    return res;}int main(){    int n,i;    int sum;    while(scanf("%d",&n)!=EOF)    {        build(0,n-1,1);        sum = 0;        for(i=0;i<n;i++)        {            scanf("%d",&a[i]);            sum += query(0,n-1,a[i],n-1,1);            update(0,n-1,a[i],1);        }        int ans = 100000000;        for(i=0;i<n;i++)        {            sum = sum+n-a[i]-1-a[i];            ans = min(ans,sum);        }        printf("%d\n",ans);    }    return 0;}
View Code

 

0 0
原创粉丝点击