这也是算法导论第一章的最后一道习题。按照算法导论中的提示,该问题可以通过修改合并排序在O(nlgn)内解决。另外一种方法,是使用一种特殊的数据结构称之为“树状数组”,也可以在O(nlgn)内解决。代码如下:
#include<iostream> #include<stdio.h> #include<string.h> using namespace std ; #define MAXN 100002 #define MAX 1000002 int n,a[MAXN],c[MAX] ; int main() { int runs ; scanf("%d",&runs) ; while(runs--) { scanf("%d",&n) ; for(int i = 0;i < n;i++) scanf("%d",&a[i]) ; long long ret = 1LL * n * (n - 1) / 2 ; memset(c,0,sizeof c) ; for(int i = 0;i < n;i++) { for(int j = a[i];j > 0;j -= j & -j) ret -= c[j] ; for(int j = a[i];j < MAX;j += j & -j) c[j]++ ; } cout << ret << endl ; } return 0 ; }
下面介绍下树状数组(Binary Indexed Tree)。如果搜一下,就会发现有很多文章详细介绍树状数组,比如这篇,所以这里不再赘述。 它能够高效地获取数组中连续n个数的和。概括说,树状数组通常用于解决以下问题:数组{a}中的元素可能不断地被修改,怎样才能快速地获取连续几个数的和? 传统数组(共n个元素)的元素修改和连续元素求和的复杂度分别为O(1)和O(n)。树状数组通过将线性结构转换成伪树状结构(线性结构只能逐个扫描元素,而树状结构可以实现跳跃式扫描),使得修改和求和复杂度均为O(lgn),大大提高了整体效率。 let the code talk.值得提一下的是,树状数组下标是从1开始的。
//求2^k int lowbit(int t) { return t & ( t ^ ( t - 1 ) ); } //求前n项和 int sum(int end) { int sum = 0; while(end > 0) { sum += in[end]; end -= lowbit(end); } return sum; } //增加某个元素的大小 void plus(int pos, int num) { while(pos <= n) { in[pos] += num; pos += lowbit(pos); } }
没有评论:
发表评论