hdu1394线段树求逆序数

一直想不明白要怎么建树,由于是满足 i < j and ai > aj的条件,所以其实就是对于每个数aj,求比它大的数的个数。建树的时候把每个节点都初始化为0,每当插入一个数,就在这个数对应的叶子节点上加1,同时更新包含这个点的线段所对应的非叶子节点(加1),一层层更新上去,区间求和。这样如果存在某个数,在相应查找的时候就会找到。由于是要找比aj大的数,所以查找范围就是aj-(n-1).

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=5005;
int num[maxn<<2];

void build(int l,int r,int rt)
{
    num[rt]=0;
    if(l==r)
    return ;
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
}

void update(int k,int l,int r,int rt)
{
    if(l==r)
    {
        num[rt]++;
        return ;
    }
    int m=(l+r)>>1;
    if(k<=m)
    update(k,l,m,rt<<1);
    else
    update(k,m+1,r,rt<<|1);
    num[rt]=num[rt<<1]+num[rt<<1|1];
}

int query(int ll,int rr,int l,int r,int rt)
{
    if(ll<=l&&rr>=r)
    return num[rt];
    int ans=0;
    int m=(l+r)>>1;
    if(l<=m)
    ans+=query(ll,rr,l,m,rt<<1);
    if(r>m)
    ans+=query(ll,rr,m+1,r,rt<<1|1);
    return ans;
}

int a[maxn];
int main()
{
    int n;
    while(scanf("%d",&n)!=-1)
    {
        int sum=0;
        build(1,n,1);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            sum+=query(a[i]+1,n,1,n,1);
            update(a[i]+1,1,n,1);
        }
        int ans=sum;
        for(int i=0;i<n;i++)
        {
            sum+=(n-a[i]*2-1);
            //公式推导,把最前面的数a[i]放到最后相当于原逆序数n个数少了a[i],多了n-a[i]+1个;
            ans=min(ans,sum);
        }
        printf("%d\n",ans);
    }
    return 0;
}

由于数列是0到n的 有规律 可以直接模拟

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
int a[5005];
int main()
{
    int n;
    while(cin>>n)
    {
        int sum=0;
        for(int i=1;i<=n;i++)
        cin>>a[i];
        for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++)
        if(a[i]>a[j])sum++;
        int minn=sum;
        for(int i=1;i<=n;i++)
        {
            sum=sum-2*a[i]+n-1;
            if(minn>sum)
            minn=sum;
        }
        cout<<minn<<endl;
    }

    return 0;
}

你可能感兴趣的:(hdu1394线段树求逆序数)