您的位置:首页 > 其它

线段树求逆序对(hdu1394Minimum Inversion Number)

2015-07-24 08:22 288 查看
        说实话,线段树求逆序对我理解了半天诶,不知是否有人像我一样。

        对于每个数来说,只有和已经出现过的、比它大的数才能形成逆序对,那么在给定的数列中,每给一个数就向前找比它大的数。

        样例:10

                    1 3 6 9 0 8 5 7 4 2

                    首先将数组清0,0~9(n-1=9):0 0 0 0 0 0 0 0 0 0

                    出现“1” ,且现在数组中没有比它大的数,逆序对数sum+0,0~9(n-1=9):0 1 0 0 0 0 0 0 0 0

                    出现“3” ,且现在数组中没有比它大的数,逆序对数sum+0,0~9(n-1=9):0 1 0 3 0 0 0 0 0 0

                    出现“6” ,且现在数组中没有比它大的数,逆序对数sum+0,0~9(n-1=9):0 1 0 3 0 0 6 0 0 0

                    出现“9” ,且现在数组中没有比它大的数,逆序对数sum+0,0~9(n-1=9):0 1 0 3 0 0 6 0 0 9

                    出现“0” ,0后有4个数,逆序对数sum+4,0~9(n-1=9):0 1 0 3 0 0 6 0 0 9

                    以此类推。在每次查找完后将这个数加入数列。

                    在把原数列找完后,要开始将第一个数挪到后面去,比这个数 x 小的有 x 个,比这个数大的有 n-1-x个,于是 sum += (n-1-x) - x 。

#include<stdio.h>
#include<algorithm>
using namespace std;
int n;
int tree[5001*4];
void pushup(int node)
{
tree[node]=tree[node<<1]+tree[node<<1|1];
return ;
}
void build(int l,int r,int node)
{
tree[node]=0;//将数组清0
if(l==r)
{
return ;
}
int mid=(l+r)>>1;
build(l,mid,node<<1);
build(mid+1,r,node<<1|1);
}
void update(int x,int l,int r,int node)
{
if(l==r)
{
tree[node]++;//是这个数出现了,并不是赋值
return ;
}
int mid=(l+r)>>1;
if(x<=mid)
{
update(x,l,mid,node<<1);
}else
{
update(x,mid+1,r,node<<1|1);
}
pushup(node);
}
int query(int l,int r,int st,int en,int node)
{
if(st>=l&&en<=r)return tree[node];
int mid=(st+en)>>1;
int ret=0;
if(mid>=l)ret+=query(l,r,st,mid,node<<1);
if(mid<r)ret+=query(l,r,mid+1,en,node<<1|1);
return ret;
}
int num[50005];
int main()
{
while(scanf("%d",&n)!=EOF)
{
int ans=0;
build(0,n-1,1);
for(int i=0;i<n;i++)
{
scanf("%d",&num[i]);
ans+=query(num[i],n-1,0,n-1,1);//询问是否有比 num[i] 大的数
update(num[i],0,n-1,1); //将 num[i] 更新
}
int ret=ans;
for(int i=0;i<n;i++)
{
ans+=n-num[i]-num[i]-1;
ret=min(ret,ans);
}
printf("%d\n",ret);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息