树状数组的应用

 

 

树状数组的应用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;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值