POJ2299 线段树求逆序数

来源:互联网 发布:2016人工智能论坛 编辑:程序博客网 时间:2024/06/05 11:27

题意:

将一个无序的序列排成有序的最小步数,该算法通过交换两个相邻的序列元素来处理n个不同的整数序列,直到序列按升序排序。

思路:

就是求序列逆序数,我的方法用线段树求解(我的逆序数模板可能跟其他大佬有一丢丢不同可以看一下这个线段树简述)。

具体:

将一个序列一个一个列出来,看“排在它前面元素有多少个比他大的”。

线段树节点的权值value表示区间[left,right]此时已经有多少个元素,所以插入一个元素X,只要知道(查询)区间(X+1,N)里有多少个元素即可,就是上面所说的“排在它前面元素有多少个比他大的”。

细节

但是得用元素下标来求逆序数(因为ai最大999,999,999;具体操作见代码),这样缩小区间最大值。

就只需要建树BuildTree(1,1,N+1),注意是1到N+1。

栗子:

num[i].x        9 1 0 5 4 (逆序数1+2+1+2 = 6)

num[i].pos    1 2 3 4 5

排序后

num[i].x       0 1 4 5 9

num[i].pos    3 2 5 4 1 (逆序数1+0+1+4 = 6)真巧啊,逆序数居然是一样的(>^v^<)

插入2:查询Query(1,3,6)得到[3,6]区间此时有几个元素,并更新UpdateTree(father[2]),

插入5:查询Query(1,6,6)得到[5,6]区间此时有几个元素,并更新UpdateTree(father[5]),

依次。。。

插入

代码:

#include <stdio.h>#include <iostream>#include <cstring>#include <stdlib.h>#include <math.h>#include <algorithm>#include <queue>//#include <bits/stdc++.h>using namespace std;const int maxn = 1<<20;const int N = 500000+5;struct Node {    int value;    int left, right;};int father[N];struct s {    int x, pos;}num[N];Node node[N*4];void BuildTree(int i, int left, int right){    node[i].left = left;  //写入第i个结点的左区间    node[i].right = right;  //写入第i个结点的右区间    node[i].value = 0;  //区间权值初始化为0    if(left == right) {        father[left] = i;  //知道每个点对应的序号(结点的下标)        return;    }    //建立左子树    BuildTree(i<<1, left, (left+right)/2);    //建立右子树    BuildTree((i<<1)+1, (left+right)/2+1, right);}bool cmp(s a, s b){    return a.x < b.x;}void UpdateTree(int ri){    if(ri == 0) return;   //已找到祖先    node[ri].value++;    UpdateTree(ri/2);  //递归更新,有父节点往上找}long long sum;void Query(int i, int l, int r){    if(node[i].left == l && node[i].right == r) {         sum += node[i].value;        return;    }    i = i << 1;    if(l <= node[i].right) {    //左区间有涉及        if(r <= node[i].right)   //全包含于左区间,则查询形态不变            Query(i, l, r);        else          //半包含于左区间,则查询区间拆分,左端点不变,右端点变为左区间的右孩子端点            Query(i, l, node[i].right);    }    i += 1;    if(r >= node[i].left) {    //右区间有涉及        if(l >= node[i].left)  //全包含于右区间,则查询形态不变            Query(i, l, r);        else        //半包含于右区间,则查询区间拆分            Query(i, node[i].left, r);    }}int main(){    //freopen("in.txt", "r", stdin);    int n;    while(scanf("%d", &n), n)    {        memset(num, 0, sizeof(num));        long long ans = 0;        for(int i = 1; i <= n; i++)        {            scanf("%d", &num[i].x);            num[i].pos = i;        }        sort(num+1, num+n+1, cmp);        BuildTree(1, 1, n+1);        for(int i = 1; i <= n; i++)        {            sum = 0;            UpdateTree(father[num[i].pos]);            Query(1, num[i].pos+1, n+1);            ans += sum;        }        printf("%I64d\n", ans);    }    return 0;}





原创粉丝点击