zju 1484 hdu 1394 求最小逆序(点树)

zju 1484 hdu 1394 求最小逆序(点树)

先说一下逆序数的概念:
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那末它们就称为一个逆序。
一个排列中逆序的总数就称为这个排列的逆序数。逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。
如2431中,21,43,41,31是逆序,逆序数是4,为偶排列。 
                                   ——这是北大《高等代数》上的定义。

题意:给出一个排列,排列中的数各不相同,都是从0到n-1,要求得到的是一个最短的逆序的长度,这些子序列是转动原来的序列得到的。
一种最基本的做法:就是枚举每一个点求出这个点前面比它的数的个数,然后加起来。还好这种做法没有超时,这里有一种O(nlog(n))的算法
就是利用点树。我在网上找过,网上关于点树的资料很少,百度百科里面有一篇。还有就是http://www.cnitblog.com/cockerel/archive/2006/09/13/16806.html
不过好像是一个人写的. @_@    ^_^
以下借用了他的实现


 1#include<iostream>
 2#include<cstdio>
 3using namespace std;
 4#define N 8192 
 5//注:N必须是2的幂,为此我在zju上wa了两次,奇怪的是在hdu上居然没有wa
 6
 7struct PointTree{
 8    int a[2*N];
 9    int size;
10    void init(){
11        size=0;
12        memset(a,0,sizeof(a));
13    }

14    void add(int n){
15        int i=N+n;size++;
16        for(++a[i];i>1;i/=2)
17            if(~i&1) a[i/2]++;
18    }
 
19
20    void del(int n){
21        int i=n+N;
22        if(!a[i]) return ;
23        size--;
24        for(--a[i];i>1;i/=2){
25            if(~i&1) a[n/2]--;
26        }

27    }

28
29    int cntLs(int n)//返回小于n的点的个数
30        int i=N+n,c=0;
31        for(;i>1;i/=2){
32            if(i&1) c+=a[i/2];
33        }

34        return c;
35    }

36    int cntGt(int n){ //返回大于n的点的个数
37        return size-a[N+n]-cntLs(n);
38    }

39}
;
40
41PointTree pt;
42int n;
43int a[N];
44
45int main()
46{
47    int x;
48    while(scanf("%d",&n) != EOF){
49        pt.init();
50        int ans=0;
51        for(int i = 0 ; i < n ; i ++){
52            scanf("%d"&a[i]);
53            ans+=pt.cntGt(a[i]);
54            pt.add(a[i]);
55        }

56        int mi=ans;
57        for(int i=0;i<n-1;i++){
58            ans=ans-a[i]+n-a[i]-1;
59            mi=min(mi,ans);
60        }

61        printf("%d\n",mi);
62    }

63    return 0;
64}




 

你可能感兴趣的:(zju 1484 hdu 1394 求最小逆序(点树))