树状数组的应用1:求逆序数
-首先考虑将输入数组离散化,因为题目要求输入的数值可以达到10的9次方,肯定不会开出那么大的数组。
1.定义一个结构体 val保存原值,pos保存原来在数组中的位置
2.在对原结构体数组对val值排序
3.定义保存离散化数据的数组flect,flect[node[i].pos] = i;
eg: 位置 : 1 2 3 4 5
原数组:9 0 1 5 4
排序后 : 0 1 4 5 9 //flect[node[i].pos] 其中i=1的时候,排序后的node[1] 原来数组中最小的数,离散化之后当然赋值为最小的1啦;
flect数组:5 1 2 4 3
-其次考虑如何利用getsum得到逆序数对的个数
1.首先初始化数组c[],一个个将flect数组插入到树上去,每插入一个数,就将该节点的值以及各父节点值+1;
2.利用getsum(flect[i])得到的值是这个数的前方有多少个小于它的数,再用i(已经插入的数的个数)减去getsum的返回值,就是比它大的数的个数
#include <bits/stdc++.h>
using namespace std;
const int AX = 50000+666;
struct Node{
int pos;
int val;
}node[AX];
int flect[AX];
int c[AX];
int n;
bool cmp(Node a,Node b){
return a.val<b.val;
}
int lowbit(int x){ /*获取某个二进制数的LowBit(奇1,偶2^n)*/
return x&(-x);
}
void update(int value,int site){
while(site<=n){
c[site] += value;
site += lowbit(site);
}
}
int getsum(int i){
int sum = 0;
while(i>=1){
sum += c[i];
i -= lowbit(i);
}
return sum;
}
int main(){
cin>>n;
long long ans = 0;
memset(node,0,sizeof(node));
memset(flect,0,sizeof(flect));
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++){
cin>>node[i].val;
node[i].pos = i;
}
sort(node+1,node+n+1,cmp);
for(int i=1;i<=n;i++){
flect[node[i].pos] = i;
}
/*for(int i=1;i<=n;i++){
cout<<flect[i]<<endl;
}*/
for(int i=1;i<=n;i++){
update(1,flect[i]);
ans += (i-getsum(flect[i]));
}
cout<<ans<<endl;
return 0;
}
树状数组的应用2:求区间最值:
-既然能用树状数组每一个节点保存区间和,那肯定也能实现保存一段区间(x-lowbit(x)+1,x)内的最值
-在更新值时,比较每一个节点的子节点,以此更新该区间内的最值
-区间和中,要查询[x,y]的区间和,是求出[1,x-1]的和与[1,y]的和,然后相减就得出了[x,y]区间的和。
而区间最值是没有这个性质的。
设query(x,y),表示[x,y]区间的最大值
因为h[y]表示的是[y,y-lowbit(y)+1]的最大值。
所以,可以这样求解:
若y-lowbit(y) > x ,则query(x,y) = max( h[y] , query(x, y-lowbit(y)) ); //在查询范围内
若y-lowbit(y) <=x,则query(x,y) = max( a[y] , query(x, y-1); //不在查询范围
这个递归求解是可以求出解的,且可以证明这样求解的时间复杂度是O((logn)^2)
int lowbit(int x)
{
return x & (-x);
}
void updata(int x)
{
for(int j=1;j<lowbit(x);j<<=1){
h[x] = min(h[x],h[x-j]);
}
}
int query(int x, int y)
{
int ans = a[y];
while (x!=y)
{
y --;
for (; y-lowbit(y) >= x; y -= lowbit(y)) // 判断此区间是否在查询范围内
ans = min(h[y], ans);
ans = min(a[y], ans); // 如果不在查询范围内,则只能将第y个数单独加入判断
}
return ans;
}
POJ3264:查询最大最小值之差
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
const int MAXN = 3e5;
int a[MAXN], h[MAXN];
int n, m;
int lowbit(int x)
{
return x & (-x);
}
void updata(int x)
{
for(int j=1;j<lowbit(x);j<<=1){
h[x] = min(h[x],h[x-j]);
}
}
int query(int x, int y)
{
int ans = a[y];
while (x!=y)
{
y --;
for (; y-lowbit(y) >= x; y -= lowbit(y)) // 1.判断此区间是否在查询范围内
ans = min(h[y], ans);
ans = min(a[y], ans);// 2.如果不在查询范围内,则只能将第y个数单独加入判断
}
return ans;
}
int main()
{
int i, j, x, y, ans;
while (scanf("%d%d",&n,&m)!=EOF)
{
for (i=1; i<=n; i++)
{
scanf("%d",&a[i]);
h[i] = a[i];
updata(i);
}
for (i=1; i<=m; i++)
{
scanf("%d%d",&x,&y);
ans = query(x, y);
printf("%d\n",ans);
}
}
return 0;
}