(转)树状数组可以用来求逆序数, 当然一般用归并求。如果数据不是很大, 可以一个个插入到树状数组中, 每插入一个数, 统计比他小的数的个数,对应的逆序为 i- getsum( data[i] ),其中 i 为当前已经插入的数的个数, getsum( data[i] )为比 data[i] 小的数的个数i- sum( data[i] ) 即比 data[i] 大的个数, 即逆序的个数但如果数据比较大,就必须采用离散化方法。一关键字的离散化方法:所谓离散化也就是建立一个一对一的映射。 因为求逆序时只须要求数据的相应
大小关系不变。如: 10 30 20 40 50 与 1 3 2 4 5 的逆序数是相同的定义一个结构体 struct Node{ int data; // 对应数据 int pos; // 数据的输入顺序 };
先对 data 升序排序, 排序后,pos 值对应于排序前 data 在数组中的位置。再定义一个数组 p[N], 这个数组为原数组的映射。以下语句将按大小关系把原数组与 1到 N 建立一一映射。
题目链接:click here
预备函数
定义一个Lowbit函数,返回参数转为二进制后,最后一个1的位置所代表的数值.
例如,Lowbit(34)的返回值将是2;而Lowbit(12)返回4;Lowbit(8)返回8。
将34转为二进制,为0010 0010,这里的"最后一个1"指的是从2^0位往前数,见到的第一个1,也就是2^1位上的1.
程序上,((Not I)+1) And I表明了最后一位1的值,
仍然以34为例,Not 0010 0010的结果是 1101 1101(221),加一后为 1101 1110(222), 把 0010 0010与1101 1110作AND,得0000 0010(2).
Lowbit的一个简便求法:(C++)
int lowbit(int x)
{
return x&(-x);
}
新建
定义一个数组 BIT,用以维护A的前缀和,则:
具体能用以下方式实现:(C++)
void build()
{
for (int i=1;i<=MAX_N;i++)
{
BIT[i]=A[i];
for (int j=i-1; j>i-lowbit(i);j-=lowbit(j))
BIT[i]+=BIT[j];
}
}
修改
假设现在要将A[I]的值增加delta,
那么,需要将BTI[I]覆盖的区间包含A[I]的值都加上K.
这个过程可以写成递归,或者普通的循环.
需要计算的次数与数据规模N的二进制位数有关,即这部分的时间复杂度是O(LogN)
修改函数的C++写法
void edit(int i, int delta)
{
for (int j = i; j <= MAX_N; j += lowbit(j))
BIT[j] += delta;
}
求和
- 首先,将ans初始化为0,将i计为k.
- 将ans的值加上BIT[P]
- 将i的值减去lowbit(i)
- 重复步骤2~3,直到i的值变为0
求和函数的C/C++写法
int sum (int k)
{
int ans = 0;
for (int i = k; i > 0; i -= lowbit(i))
ans += BIT[i];
return ans;
}
复杂度
初始化复杂度最优为O(N)
单次询问复杂度O(LOG(N))其中N为数组大小
单次修改复杂度O(LONG(N))其中N为数组大小
空间复杂度O(N);
代码:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1000001;
const double eps=1e-6;
const double pi=acos(-1.0);
#define lowbit(x) ((x)&(-x))
struct node
{
int data,pos;
} doo[maxn];
int n;
int coo[maxn],poo[maxn];
int cmp(const void *a,const void *b)
{
node *ta=(node *)a;
node *tb=(node*)b;
return ta->data-tb->data;
}
int cmp1(node aa,node bb)
{
return aa.data-bb.data;
}
void updata(int pos,int value)
{
int x=pos;
while(x<=n)
{
coo[x]+=value;
x+=lowbit(x);
}
}
int getsum(int pos)
{
int x=pos,sum=0;
while(x)
{
sum+=coo[x];
x-=lowbit(x);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d",&doo[i].data);
doo[i].pos=i;
}
qsort(doo+1,n,sizeof(doo[0]),cmp);
// sort(doo,doo+n,cmp1);
int id=1;
poo[doo[1].pos]=1;
for(int i=2; i<=n; i++)
if(doo[i].data==doo[i-1].data) poo[doo[i].pos]=id;
else poo[doo[i].pos]=++id;
memset(coo,0,sizeof(coo));
long long ans=0;
for(int i=1; i<=n; i++)
{
updata(poo[i],1);
ans+=(long long )(i-getsum(poo[i]));
}
printf("%lld\n",ans);
// int n,s=0;; //暴力
// int a[100];
// scanf("%d",&n);
// for(int i=0; i<n; i++)
// scanf("%d",&a[i]);
// for(int i=0; i<n; i++)
// for(int j=i+1; j<n; j++)
// {
// if(a[j]<a[i])
// s++;
// }
// printf("%d\n",s);
// }
//
}
return 0;
}
归并排序合并算法
#include <stdio.h>
#include <string.h>
#define MAXM 1000003
#define INF 0x7fffffff-1;
long long cnt;
int arr[MAXM];
int temp1[MAXM/2+1], temp2[MAXM/2+1];
void Merge(int array[], int start, int mid, int end)// 归并排序中的合并算法
{
int n1, n2,i,k,j;
n1 = mid - start + 1;
n2 = end - mid;
for (i = 0; i < n1; i++) // 拷贝前半部分数组
{
temp1[i] = array[start + i];
}
for ( i = 0; i < n2; i++) // 拷贝后半部分数组
{
temp2[i] = array[mid + i + 1];
}
temp1[n1] = temp2[n2] = INF; // 把后面的元素设置的很大
for ( k = start, i = 0, j = 0; k <= end; k++)// 逐个扫描两部分数组然后放到相应的位置去
{
if (temp1[i] <= temp2[j])
{
array[k] = temp1[i];
i++;
}
else
{
cnt+=n1-i; //逆序对的个数
array[k] = temp2[j];
j++;
}
}
}
// 归并排序
void MergeSort(int array[], int start, int end)
{
if (start < end)
{
int i;
i = (end + start) / 2;
// 对前半部分进行排序
MergeSort(array, start, i);
// 对后半部分进行排序
MergeSort(array, i + 1, end);
// 合并前后两部分
Merge(array, start, i, end);
}
}
int main()
{
//freopen("11.txt","r",stdin);
//freopen("2.txt","w",stdout);
int T,i,n;
scanf("%d",&T);
while(T--)
{
cnt=0;
scanf("%d",&n);
for(i=0; i<n; i++)
{
scanf("%d",arr+i);
}
MergeSort(arr,0,n-1);
printf("%lld\n",cnt);
}
return 0;
}