NYOJ117&& 树状数组求逆序数

(转)树状数组可以用来求逆序数, 当然一般用归并求。如果数据不是很大, 可以一个个插入到树状数组中, 每插入一个数, 统计比他小的数的个数,对应的逆序为 i- getsum( data[i] ),其中 i 为当前已经插入的数的个数, getsum( data[i] )为比 data[i] 小的数的个数i- sum( data[i] ) 即比 data[i] 大的个数, 即逆序的个数但如果数据比较大,就必须采用离散化方法。一关键字的离散化方法:所谓离散化也就是建立一个一对一的映射。
因为求逆序时只须要求数据的相应

大小关系不变。如: 10 30 20 40 50  与  1 3 2 4 5 的逆序数是相同的定义一个结构体  struct Node{ int data;    // 对应数据       int pos;     // 数据的输入顺序 };

先对 data 升序排序, 排序后,pos 值对应于排序前 data 在数组中的位置。再定义一个数组 p[N], 这个数组为原数组的映射。以下语句将按大小关系把原数组与 1到 N 建立一一映射。

题目链接:click here

预备函数

定义一个Lowbit函数,返回参数转为二进制后,最后一个1的位置所代表的数值.

例如,Lowbit(34)的返回值将是2;而Lowbit(12)返回4;Lowbit(8)返回8。

将34转为二进制,为0010 0010,这里的"最后一个1"指的是从2^0位往前数,见到的第一个1,也就是2^1位上的1.

程序上,((Not I)+1) And I表明了最后一位1的值,

仍然以34为例,Not 0010 0010的结果是 1101 1101(221),加一后为 1101 1110(222), 把 0010 0010与1101 1110作AND,得0000 0010(2).

Lowbit的一个简便求法:(C++)

int lowbit(int x)
{
    return x&(-x);
}

新建

定义一个数组 BIT,用以维护A的前缀和,则:

具体能用以下方式实现:(C++)

void build()
{
    for (int i=1;i<=MAX_N;i++)
    {
        BIT[i]=A[i];
        for (int j=i-1; j>i-lowbit(i);j-=lowbit(j))
            BIT[i]+=BIT[j];
    }
}

修改

假设现在要将A[I]的值增加delta,

那么,需要将BTI[I]覆盖的区间包含A[I]的值都加上K.

这个过程可以写成递归,或者普通的循环.

需要计算的次数与数据规模N的二进制位数有关,即这部分的时间复杂度是O(LogN)

修改函数的C++写法

void edit(int i, int delta)
{
    for (int j = i; j <= MAX_N; j += lowbit(j))
        BIT[j] += delta;
}

求和

  1. 首先,将ans初始化为0,将i计为k.
  2. 将ans的值加上BIT[P]
  3. 将i的值减去lowbit(i)
  4. 重复步骤2~3,直到i的值变为0

求和函数的C/C++写法

int sum (int k)
{
    int ans = 0;
    for (int i = k; i > 0; i -= lowbit(i))
        ans += BIT[i];
    return ans;
}

复杂度

初始化复杂度最优为O(N)

单次询问复杂度O(LOG(N))其中N为数组大小

单次修改复杂度O(LONG(N))其中N为数组大小

空间复杂度O(N);

代码:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1000001;
const double eps=1e-6;
const double pi=acos(-1.0);
#define lowbit(x) ((x)&(-x))
struct node
{
    int data,pos;
} doo[maxn];
int n;
int coo[maxn],poo[maxn];
int cmp(const void *a,const void *b)
{
    node *ta=(node *)a;
    node *tb=(node*)b;
    return ta->data-tb->data;
}
int cmp1(node aa,node bb)
{
    return aa.data-bb.data;
}
void updata(int pos,int value)
{
    int x=pos;
    while(x<=n)
    {
        coo[x]+=value;
        x+=lowbit(x);
    }
}
int getsum(int pos)
{
    int x=pos,sum=0;
    while(x)
    {
        sum+=coo[x];
        x-=lowbit(x);
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&doo[i].data);
            doo[i].pos=i;
        }
        qsort(doo+1,n,sizeof(doo[0]),cmp);
        // sort(doo,doo+n,cmp1);
        int id=1;
        poo[doo[1].pos]=1;
        for(int i=2; i<=n; i++)
            if(doo[i].data==doo[i-1].data) poo[doo[i].pos]=id;
            else poo[doo[i].pos]=++id;
        memset(coo,0,sizeof(coo));
        long long ans=0;
        for(int i=1; i<=n; i++)
        {
            updata(poo[i],1);
            ans+=(long long )(i-getsum(poo[i]));
        }
        printf("%lld\n",ans);

//        int n,s=0;;                  //暴力
//        int a[100];
//        scanf("%d",&n);
//        for(int i=0; i<n; i++)
//            scanf("%d",&a[i]);
//        for(int i=0; i<n; i++)
//            for(int j=i+1; j<n; j++)
//            {
//                if(a[j]<a[i])
//                    s++;
//            }
//            printf("%d\n",s);
//    }
//
    }
    return 0;
}

归并排序合并算法

#include <stdio.h>
#include <string.h>
#define MAXM 1000003
#define INF 0x7fffffff-1;
long long cnt;
int arr[MAXM];
int temp1[MAXM/2+1], temp2[MAXM/2+1];

void Merge(int array[], int start, int mid, int end)// 归并排序中的合并算法
{
    int n1, n2,i,k,j;
    n1 = mid - start + 1;
    n2 = end - mid;

    for (i = 0; i < n1; i++)                       // 拷贝前半部分数组
    {
        temp1[i] = array[start + i];
    }

    for ( i = 0; i < n2; i++)                     // 拷贝后半部分数组
    {
        temp2[i] = array[mid + i + 1];
    }

    temp1[n1] = temp2[n2] = INF;               // 把后面的元素设置的很大

    for ( k = start, i = 0, j = 0; k <= end; k++)// 逐个扫描两部分数组然后放到相应的位置去
    {
        if (temp1[i] <= temp2[j])
        {
            array[k] = temp1[i];
            i++;
        }
        else
        {
            cnt+=n1-i;                            //逆序对的个数
            array[k] = temp2[j];
            j++;
        }
    }
}

// 归并排序
void MergeSort(int array[], int start, int end)
{
    if (start < end)
    {
        int i;
        i = (end + start) / 2;
                                                   // 对前半部分进行排序
        MergeSort(array, start, i);
                                                   // 对后半部分进行排序
        MergeSort(array, i + 1, end);
                                                   // 合并前后两部分
        Merge(array, start, i, end);
    }
}
int main()
{
    //freopen("11.txt","r",stdin);
    //freopen("2.txt","w",stdout);
    int T,i,n;
    scanf("%d",&T);
    while(T--)
    {
        cnt=0;
        scanf("%d",&n);
        for(i=0; i<n; i++)
        {
            scanf("%d",arr+i);
        }
        MergeSort(arr,0,n-1);
        printf("%lld\n",cnt);
    }
    return 0;
}
时间: 2024-08-16 13:24:11

NYOJ117&& 树状数组求逆序数的相关文章

ZOJ-2386 Ultra-QuickSort 【树状数组求逆序数+离散化】

Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input seque

树状数组求逆序数

poj 2299 树状数组求逆序数题目链接:http://poj.org/problem?id=2299 1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <algorithm> 5 #include <vector> 6 #include <queue> 7 #include <stack> 8 #include <

HDU 1394 Minimum Inversion Number (树状数组求逆序数)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 13942    Accepted Submission(s): 8514 Problem Description The inversion number of a given number sequence a1, a2, ..., a

hdu 5147 Sequence II (树状数组 求逆序数)

题目链接 Sequence II Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 331    Accepted Submission(s): 151 Problem Description Long long ago, there is a sequence A with length n. All numbers in this se

hdu 1394 Minimum Inversion Number (裸树状数组 求逆序数)

题目链接 题意: 给一个n个数的序列a1, a2, ..., an ,这些数的范围是0-n-1, 可以把前面m个数移动到后面去,形成新序列: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)求这些序列中,逆序数最少的

poj2299 Ultra-QuickSort 树状数组求逆序数

poj2299 Ultra-QuickSort   树状数组求逆序数 Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 49587   Accepted: 18153 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequenc

Codeforces Round #261 (Div. 2) D. Pashmak and Parmida&#39;s problem (树状数组求逆序数 变形)

题目链接 题意: 给出一些数a[n],求(i, j), i<j 的数量,使得:f(1, i, a[i]) > f(j, n, a[j]) . f(lhs, rhs, x) 指在 { [lhs, rhs]范围中,a[k]的值=x } 的数量. 1.  f(1, i, a[i]) 就是指a[i]前面包括a[i]的数中,有几个值=a[i]. 2.  f(j, n, a[j]) 就是指a[j]后面包括a[j]的数中有几个值=a[j]. 虽然a[x]范围不小,但是n的范围是1000,不是很大,所以我们可

Dynamic Inversions II 逆序数的性质 树状数组求逆序数

Dynamic Inversions II Time Limit: 6000/3000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) SubmitStatus Problem Description 给出N个数a[1],a[2] ... a[N],a[1]...a[N]是1-N的一个排列,即1 <= a[i] <= N且每个数都不相同.有M个操作,每个操作给出x,y两个数,你将a[x],a[y]交换,然后求交换后数组的逆序

poj 2299 树状数组求逆序数+离散化

http://poj.org/problem?id=2299 最初做离散化的时候没太确定但是写完发现对的---因为后缀数组学的时候,,这种思维习惯了吧 1.初始化as[i]=i:对as数组按照num[]的大小间接排序 2.bs[as[i]]=i:现在bs数组就是num[]数组的离散化后的结果 3.注意,树状数组中lowbit(i)  i是不可以为0的,0&(-0)=0,死循环... #include <cstdio> #include <cstring> #include