hdu1394线段树求逆序数

来源:互联网 发布:私有云平台网络拓扑图 编辑:程序博客网 时间:2024/05/18 01:45

一直想不明白要怎么建树,由于是满足 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;}
0 0