您的位置:首页 > 其它

hdu 1394 Minimum Inversion Number

2011-04-15 10:30 330 查看
首先,我们可以把初始序列的逆序对数求出来(暴力,线段树,树状数组什么的都可以),然后根据序列特殊的性质进行O(N)的转移和更新

先说下线段树怎么求逆序对把,本质上还是用了树状数组的思想,首先建立一个线段树,每个线段上储存一个sum值,表示这个线段上的所有数总共出现的次数,于是我们每读到一个数ai,就去查询[ai+1,n](注意ai=n的情况)这个区间上在读入前i-1个数后的sum值,这个就是第i个数对全局逆序对数的贡献

然后,对[ai,ai]这个区间进行更新,sum值加1,并向上递归更新父区间

由于n个数的特殊性,分别是[0,n-1]中的n个数,每个数都出现一次,且无重复

那么,我们每次把当前第一个数a移到最后一个时,逆序对数减少x个,同时又增加y个

其中,x等于[0,n-1]中比a小的个数,y等于[0,n-1]中比a大的个数

于是,就是进行O(N)的转移和更新答案了

代码:

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<math.h>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
const int MAX=5005;
const int inf=1<<30;
struct node
{
int l,r,sum;
}t[MAX*5];
int val[MAX];
int n;
void build(int ll,int rr,int n)
{
t
.l=ll; t
.r=rr; t
.sum=0;
if(ll==rr)
return;
int mid=(ll+rr)/2;
build(ll,mid,n*2);
build(mid+1,rr,n*2+1);
}
void update(int ll,int rr,int n)
{
if(t
.l==ll&&t
.r==rr)
{
t
.sum++;
return;
}
int mid=(t
.l+t
.r)/2;
if(mid>=rr)
update(ll,rr,n*2);
else
update(ll,rr,n*2+1);
t
.sum=t[n*2].sum+t[n*2+1].sum;
}
int query(int ll,int rr,int n)
{
if(t
.l==ll&&t
.r==rr)
{
return t
.sum;
}
int mid=(t
.l+t
.r)/2;
if(mid>=rr)
return query(ll,rr,n*2);
else if(mid<ll)
return query(ll,rr,n*2+1);
else
return query(ll,mid,n*2)+query(mid+1,rr,n*2+1);
}
int main()
{
int i,j,res;
while(scanf("%d",&n)!=EOF)
{
build(1,n,1);
res=0;
for(i=1;i<=n;i++)
{
scanf("%d",&val[i]);
val[i]++;
if(val[i]==n)
{
//update(n,n,1);
j=0;
res+=j;
}
else
{
j=query(val[i]+1,n,1);
res+=j;
}
update(val[i],val[i],1);
//cout<<"i="<<i<<endl;
//cout<<"j="<<j<<endl;
}
int ans=res;
for(i=1;i<=n;i++)
{
res=res-(val[i]-1)+(n-val[i]);
ans=min(res,ans);
}
printf("%d/n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: