您的位置:首页 > 其它

PKU 2299 求解逆序数(使用归并或者树状数组) 树状数组及入门知识

2013-01-11 15:41 323 查看
归并ac代码为:

#include <iostream>
#include <fstream>
using namespace std;

#define MAX 500005
int a[MAX], t[MAX];
__int64 cnt;

void Merge(int l, int mid, int r)
{
int i = l, j = mid + 1, k = 0;
while(i <= mid && j <= r) {
if(a[i] > a[j]) {
t[k++] = a[j++];
cnt += mid - i + 1;
} else {
t[k++] = a[i++];
}
}
while(i <= mid) t[k++] = a[i++];
while(j <= r) t[k++] = a[j++];
for(i = 0; i < k; i++)
a[l+i] = t[i];
}

void MergeSort(int l, int r)
{
int mid = (l + r) / 2;
if(l < r) {
MergeSort(l, mid);
MergeSort(mid+1, r);
Merge(l, mid, r);
}
}

int main()
{
int n, i;
while(scanf("%d", &n) != EOF && n) {
for(i = 0; i < n; i++)
scanf("%d", &a[i]);
cnt = 0;
MergeSort(0, n-1);
printf("%I64d\n", cnt);
}
return 0;
}


树状数组主要是这张图片:





inline int Lowbit(int x)
{
return x & (-x);
}
void Update(int x, int c)
{
int i;
for (i = x; i < maxn; i += Lowbit(i))
{
tree[i] += c;
}
}
int Getsum(int x)
{
int i;
int temp(0);
for (i = x; i >= 1; i -= Lowbit(i))
{
temp += tree[i];
}
return temp;
}
以上三个函数可以说是树状数组的“看家本事”,树状数组的高效就体现在这三个函数上了。

Lowbit(x),是求出2^p(其中p为x的二进制表示中最右边的那个1的位置),如6的二进制表示为110,最右边的1为1,故Lowbit(6) = 2^1 = 2。

Update(x, c),是使x这点的值改变c,如果是一般数组改变的就是x自己这点,但是树状数组中要把(x, x+Lowbit(x), x+Lowbit(x+Lowbit(x))),…)这条路径的点都要改变c,这样做是为了后面能够高效地求和。(注意x的值可能是实际的值,也可能是数组的下标,如果太大的话需要离散化)

Getsum(x), 是求的(1, …x-Lowbit(x-Lowbit(x))), x-Lowbit(x), x)这条路径的点的和,换句话说就相当于求一般数组a[1]到a[x]的和。

树状数组的高效就在于: 与一般数组不同,一般数组都是下标不断加一来遍历的,而树状数组是不断加2^p来变化的,故效率为(logn)级别的。

树状数组的最基本功能就是求比某点x小的点的个数(这里的比较是抽象的概念,可以使数的大小,坐标的大小,质量的大小等)。

比如给定个数组a[5] = {2, 5, 3, 4, 1},求b[i] = 位置i左边小于等于a[i]的数的个数.如b[5] = {0, 1, 1, 2, 0},这是最正统的树状数组的应用,直接遍历遍数组,每个位置先求出Getsum(a[i]),然后再修改树状数组Update(a[i], 1)即可。当数的范围比较大时需要进行离散化,即先排个序,再重新编号。如a[] = {10000000, 10, 2000, 20, 300},那么离散化后a[] = {5, 1, 4, 2, 3}。(本题就是这个例子)

详细资料:http://download.csdn.net/detail/vsooda/4985249

#include <iostream>
#include <algorithm>
#define MAX 500010
using namespace std;

typedef struct ARR{
int n,ind;
}ARR;
ARR a[MAX];
long long sum;
int c[MAX],aa[MAX]; //c保存交换次数, aa保存离散化结果
bool cmp( ARR a, ARR b )
{
return a.n > b.n;
}
int Lowbit(int x)
{
return x & (-x);
}
void Update(int x)
{
while( x < MAX )
{
c[x]++;
x += Lowbit(x);
}
}
int Getsum(int x)
{
int sum = 0;
while( x > 0 )
{
sum += c[x];
x -= Lowbit(x);
}
return sum;
}
int main()
{
int n,i,tmp;
while( ~scanf("%d",&n) && n )
{
sum = 0ll;
memset(c,0,sizeof(c));
for(i=1; i<=n; i++)
{
scanf("%d",&a[i].n);
a[i].ind = i; //记住下标
}

sort(a+1,a+n+1,cmp);

int p = 1;
aa[ a[1].ind ] = p;
tmp = a[1].n;
for(i=1; i<=n; i++) //离散化
{
if( a[i].n == tmp )
aa[ a[i].ind ] = p;
else
{
tmp = a[i].n;
p++;
aa[ a[i].ind ] = p;
}
}
for(i=1; i<=n; i++)
{
sum += Getsum(aa[i]);
Update(aa[i]);
}
printf("%lld\n",sum);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: