codevs1688 求逆序对(权值线段树)

1688 求逆序对

 

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 黄金 Gold
 
 
题目描述 Description

给定一个序列a1,a2,…,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目

 

数据范围:N<=105。Ai<=105。时间限制为1s。

 

输入描述 Input Description

第一行为n,表示序列长度,接下来的n行,第i+1行表示序列中的第i个数。

输出描述 Output Description

所有逆序对总数.

样例输入 Sample Input

4

3

2

3

2

样例输出 Sample Output

3

 

/*
权值线段树模板 
首先我们更改线段树叶子节点处存储的内容,将其更改为权值,并且记录对于一个权值x的个数。
那么我们首先构建一棵空树,
对于每次插入的数字x,我们查找x+1到max区间的数字存在的个数,并将这个个数加入答案,
然后插入这个数字。最后输出答案就可以了。。
*/
#include<iostream>
#include<cstdio>
#include<cstring>

#define N 200007

using namespace std;
int n,x;
long long ans;
struct tree
{
    int l,r;
    long long sum;
}tr[N<<2];

void push_up(int now)
{
    tr[now].sum=tr[now<<1].sum+tr[now<<1|1].sum;
}

void build(int now,int l,int r)
{
    tr[now].l=l;tr[now].r=r;
    if(l==r) return;
    int mid=(tr[now].l+tr[now].r)>>1;
    build(now<<1,l,mid);build(now<<1|1,mid+1,r);
}

void insert(int now,int k)
{
    if(tr[now].l==k && tr[now].l==tr[now].r) 
    {
         tr[now].sum++;
         return;
    } 
    int mid=(tr[now].l+tr[now].r)>>1;
    if(x>mid) insert(now<<1|1,k);
    if(x<=mid) insert(now<<1,k);
    push_up(now);
}

long long gets(int now,int l,int r)
{
    if(tr[now].l>r||tr[now].r<l) return 0;
    if(tr[now].l==l&&tr[now].r==r)
    return tr[now].sum;
    int mid=(tr[now].l+tr[now].r)>>1;
    if(l>mid) return gets(now<<1|1,l,r);
    else if(r<=mid) return gets(now<<1,l,r);
    else return gets(now<<1,l,mid)+gets(now<<1|1,mid+1,r);
}

int main()
{
    scanf("%d",&n);
    build(1,1,N);ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        ans+=gets(1,x+1,N);
        insert(1,x);
    }
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2017-07-27 15:16  安月冷  阅读(247)  评论(0编辑  收藏  举报