本来是想练一下线段树的,看到这题逆序对,不用下树状数组都可惜了啊!
用树状数组很简单,就中间有一个细节,因为数组中是有0的,在我进行区间求和的时候,判断条件是x>=0,而lowb(0)==0,这就会使程序陷入死循环!注意!
因为这题是求数组a位置i右边比a[i]大的数的个数,按照网上的说法(http://apps.hi.baidu.com/share/detail/16352612):
如果要求b[i] = 位置i左边大于等于a[i]的数的个数呢?当然我们可以离散化时倒过来编号,但有没有更直接的方法呢?答案是有。几乎所有教程上树状数组的三个函数都是那样写的,但我们可以想想问啥修改就是x不断增加,求和就是x不断减少,我们是否可以反过来呢,答案是肯定的。代码如下:
代码
void Update(int x, int c)
{
int i;
for (i = x; i >= 1; i -= Lowbit(i))
{
tree[i] += c;
}
}
int Getsum(int x)
{
int i;
int temp(0);
for (i = x; i < maxn; i += Lowbit(i))
{
temp += tree[i];
}
return temp;
}
今天我写程序运行了一下,感觉这个观点并不是对的。我们应该还记得树状数组那张标志性的图(可以去看看):
树状数组之所以快,是因为它的结构很好,它的C[4]表示前4个数,C[8]表示前8个数……再写博客的时候,突然觉得是对的。它的修改时往下修改,每一个比它小的数都会有+1的,也只有比它小的才有+1。而求和的话是看比这个数大的数在前面出现几次,在修改操作中已经确定,这个是符合前面的逻辑的!
带上1094的树状数组代码吧:
代码
#include <iostream>
#include <cstdio>
using namespace std;
#define min(c,d) (c>d?d:c)
int n,S,mmin;
int C[5005];
int a[5005];
int lowb(int t)
{
return t&(-t);
}
void up(int i,int val)
{
for(;i<=n;i+=lowb(i))
{
C[i] += val;
}
}
int down(int i)
{
int sum = 0;
for(;i>0;i-=lowb(i))
{
sum += C[i];
}
return sum;
}
void Init()
{
int i;
S = 0;
memset(C,0,sizeof(C));
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i] += 1; //因为有0的存在,在求和的时候判断条件是i>=0,会陷入死循环,所以加1
}
}
void Play()
{
int i;
for(i=n;i>=1;i--)
{
S += down(a[i]);
up(a[i],1);
}
mmin = S;
for(i=1;i<=n;i++)
{
S = S - (a[i] - 1) //移动前左边比a[i]小的数
+ (n - a[i]); //移动后右边比a[i]大的数
mmin = min(mmin,S);
}
}
void print()
{
printf("%d\n",mmin);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
Init();
Play();
print();
}
}
线段树代码:
代码
#include <iostream>
#include <cstdio>
using namespace std;
#define min(c,d) (c>d?d:c)
const long maxn = 5005;
int n;
int mmin;
int a[maxn];
struct node
{
int l;
int r;
int sum;
}inv[maxn*4];
void CreateTree(int ini,int a,int b)
{
inv[ini].l = a;
inv[ini].r = b;
inv[ini].sum = 0;
if(a == b)
return;
int mid = (a+b) >> 1;
CreateTree(ini*2,a,mid);
CreateTree(ini*2+1,mid+1,b);
}
void Updata(int ini,int x,int val)
{
if(inv[ini].l == x && inv[ini].r == x)
{
inv[ini].sum += val;
return;
}
int mid = (inv[ini].l + inv[ini].r) >> 1;
if(x <= mid)
{
Updata(ini*2,x,val);
inv[ini].sum += val;
}
else
{
Updata(ini*2+1,x,val);
inv[ini].sum += val;
}
}
int Find(int ini,int x,int y)
{
if(inv[ini].l == x && inv[ini].r == y)
{
return inv[ini].sum;
}
int mid = (inv[ini].l + inv[ini].r) >> 1;
if(y <= mid)
{
return Find(ini*2,x,y);
}
else if(x > mid)
{
return Find(ini*2+1,x,y);
}
else
{
return Find(ini*2,x,mid) + Find(ini*2+1,mid+1,y);
}
}
void Init()
{
int i;
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
}
void Play()
{
int i;
int sum = 0;
CreateTree(1,0,n);
for(i=n-1;i>=0;i--)
{
sum += Find(1,0,a[i]);
Updata(1,a[i],1);
}
mmin = sum;
for(i=0;i<n;i++)
{
sum = sum - a[i] + (n-a[i]-1);
mmin = min(mmin,sum);
}
}
void print()
{
printf("%d\n",mmin);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
Init();
Play();
print();
}
}