您的位置:首页 > 其它

线段树专辑——hdu 1394 Minimum Inversion Number

2011-11-09 20:35 537 查看
http://acm.hdu.edu.cn/showproblem.php?pid=1394

这道题属于利用线段树求解一个序列的逆序数的应用。由于题目要求有5000个数,普通方法求解逆序数必然导致超时( 时间为n*(n-1)/2 )。

那么,利用线段树又如何求解呢?

这里思维有些巧,假设有序列5 3 4 1 2。

我们首先在线段[5,5]的位置上插入一个1,表示[5,5]这一线段有一个数,这个数就是5。

接下来我们要插入3,在插入3之前,我们先查找一下[4,n]这一区间,看其中有多少个1,有多少个1,就表示4~n之内有多少个数在之前已经出现过了。这个值表示3的逆序数

同理,一直插完最后一个数,我们便可以得到整个序列的逆序数。

View Code

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

struct node
{
int l;
int r;
int val;
};

node tree[25000];
int n,num[5001];

void build(int i,int l,int r)
{
tree[i].l=l;
tree[i].r=r;
tree[i].val=0;
if(l==r)
return;
int mid=(l+r)/2;
build(2*i,l,mid);
build(2*i+1,mid+1,r);
}

void updata(int i,int l,int r,int w)
{
if(tree[i].l>r || tree[i].r<l)
return;
if(tree[i].l>=l && tree[i].r<=r)
{
tree[i].val+=w;
return;
}
updata(2*i,l,r,w);
updata(2*i+1,l,r,w);
tree[i].val=tree[2*i].val+tree[2*i+1].val;
}

int find(int i,int l,int r)
{
if(tree[i].l>r || tree[i].r<l)
return 0;
if(tree[i].l>=l && tree[i].r<=r)
{
return tree[i].val;
}
return find(2*i,l,r)+find(2*i+1,l,r);
}

int main()
{
int i;
freopen("in.txt","r",stdin);
while(scanf("%d",&n)!=EOF)
{
build(1,0,n-1);
int ans=0;
for(i=1;i<=n;i++)
{
scanf("%d",&num[i]);
ans+=find(1,num[i]+1,n-1);
updata(1,num[i],num[i],1);
}
int min=ans;
for(i=1;i<n;i++)
{
ans=ans-num[i]+(n-1-num[i]);
if(ans<min)
min=ans;
}
printf("%d\n",min);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: