POJ 2299 Ultra-QuickSort(线段树+离散化)

题目地址:POJ 2299

这题以前用归并排序做过。线段树加上离散化也能够做。一般线段树的话会超时。

这题的数字最大到10^10次方,显然太大,可是能够利用下标,下标总共仅仅有50w。能够从数字大的開始向树上加点,然后统计下标比它小即在它左边的数的个数。由于每加一个数的时候。比该数大的数已经加完了,这时候坐标在它左边的就是一对逆序数。

可是该题另一个问题,就是数字反复的问题。这时候能够在排序的时候让下标大的在前面,这样就能够保证加点的时候下标比他小的数中不会出现反复的。

这题须要注意的是要用__int64。

代码例如以下:

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <queue>
#include <map>
#include <set>
#include <algorithm>

using namespace std;
#define LL __int64
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
LL sum[2100000];
struct node
{
    LL x, id;
} fei[600000];
bool cmp(node x, node y)
{
    if(x.x==y.x)
        return x.id>y.id;
    return x.x>y.x;
}
void PushUp(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(LL x, int l, int r, int rt)
{
    if(l==r)
    {
        sum[rt]++;
        return ;
    }
    int mid=l+r>>1;
    if(x<=mid) update(x,lson);
    else update(x,rson);
    PushUp(rt);
}
LL query(int ll, int rr, int l, int r, int rt)
{
    if(ll<=l&&rr>=r)
    {
        return sum[rt];
    }
    LL ans=0;
    int mid=l+r>>1;
    if(ll<=mid) ans+=query(ll,rr,lson);
    if(rr>mid) ans+=query(ll,rr,rson);
    return ans;
}
int main()
{
    int n, i, j;
    LL ans;
    while(scanf("%d",&n)!=EOF&&n)
    {
        memset(sum,0,sizeof(sum));
        for(i=1; i<=n; i++)
        {
            scanf("%I64d",&fei[i].x);
            fei[i].id=i;
        }
        sort(fei+1,fei+n+1,cmp);
        ans=0;
        for(i=1; i<=n; i++)
        {
            ans+=query(1,fei[i].id,1,n,1);
            update(fei[i].id,1,n,1);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


posted @ 2017-07-09 16:57  wzjhoutai  阅读(137)  评论(0编辑  收藏  举报