HDU 1394 树状数组+离散化求逆序数

对于求逆序数问题,学会去利用树状数组进行转换求解方式,是很必要的。

一般来说我们求解逆序数,是在给定一串序列里,用循环的方式找到每一个数之前有多少个比它大的数,算法的时间复杂度为o(n2)。

那么我们通过树状数组可以明显提高时间效率。

我们可以按照排列的顺序依次将数字放入树状数组中,并依次更新预与之相关联的树状数组元素。那么在将其更新完毕后,我们知道每个数对应的树状数组元素的左边的数肯定比它小,我们在以序列顺序依次更新树状数组时,如果有值在它前面出现,那么它对应的树状数组元素(在这个题目里存放的是个数)值必然增加,我们可以利用树状数组快速求一段区间的总和(这一段是比它小的数字的总个数)。那么用i-sum得到的就是逆序数了。

当然,另外还要注意到一点由于数字可能很大,那么我们开那么大的数组是不合理地,为此我们用离散化的方式来处理r[num[i].tag]=i;r[MAXN]存放离散数据,即用i=1;i<=t;i++从小到大表示递增的数据,以便充分使用每一个数组元素,类似于map的功能,映射。)

所以因为有这一步,所以要先sort,然后将数值离散化。这样每一个数的更新仅需o(log(n))

总的时间降为o(nlog(n))。

带着代码再加深理解:

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 5005
using namespace std;
struct node
{
int val;
int tag;
}num[MAXN];
int t;
int r[MAXN],c[MAXN];//c[MAXN]为已经离散化的树状数组元素
bool cmp(node a,node b)
{
return a.val<b.val;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x)
{
while(x<=t)
{
c[x]+=;
x+=lowbit(x);
}
}
int s(int x)
{
int sum=;
while(x>)
{
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
int main()
{
int i,j,ans,min;
while(cin>>t)
{
ans=;
memset(c,,sizeof(c));
for(i=;i<=t;i++)
{
scanf("%d",&num[i].val);
num[i].tag=i;
}
sort(num+,num+t+,cmp);
for(i=;i<=t;i++)
{
r[num[i].tag]=i;//进行离散化
}
for(i=;i<=t;i++)
{
update(r[i]);
ans=ans+i-s(r[i]);
}
min=ans;
for(i=;i<t;i++)
{
ans=ans-r[i]++t-r[i];
min=ans<min?ans:min;
}
printf("%d\n",min);
}
return ;
}
上一篇:SDUT 2141 【TEST】数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历


下一篇:SDUT 2772 数据结构实验之串一:KMP简单应用