hdu 1094 所想到的

  本来是想练一下线段树的,看到这题逆序对,不用下树状数组都可惜了啊!

  用树状数组很简单,就中间有一个细节,因为数组中是有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();
}
}

 

你可能感兴趣的:(HDU)