树状数组浅析

树状数组

树状数组可以将一整个区间划分为几个区间,T[i]包含的A[]原数组数量为 lowbit(i),

单点更新,区间查询

//单点更新操作 
void update(int i, int x)
{
	for(; i <= n; i += lowbit(i))
	{
		t[i] += x;
	}
}
//区间查询操作(求1-r的和,即r的前缀和)
int getsum(int r)
{
	int ans = 0;
	for(int i = r; i > 0; i -= lowbit(i))
	{
		ans += c[i];
	}
	return ans;
}

求[l, r]的区间和

ans = getsum(r) - getsum(l - 1);

区间更新,单点查询

可以利用差分的思想,利用树状数组维护差分数组,

更新[l, r]的区间和时,每次只需更新t[l]和 t[r+1]的值,并通过树状数组维护;

单点查询x时,根据差分数组的性质,只需求x的前缀和即可

For(i, 1, n)
	{
		cin >> a[i];
		add(i, a[i] - a[i-1]); //差分数组
	}

	while(m--)
	{
		int opt;
		int x, y, k;
		cin >> opt;
		if(opt == 1)
		{
			cin >> x >> y >> k;
			add(x, k);
			add(y + 1, -k);
		}
		if(opt == 2)
		{
			cin >> x;
			cout << quire(x) << endl;
		}
	}

求逆序对

例:洛谷P1908 逆序对

//#pragma comment(linker,   "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)

#include <bits/stdc++.h>

#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));

typedef long long ll;
typedef unsigned long long ull;
		
using namespace std;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;

struct node
{
	ll val, id;
};

ll n;
ll t[500005];
ll rankk[500005];
node a[500005];

bool cmp(node a, node b)
{
	if(a.val == b.val)
		return a.id > b.id;
	return a.val > b.val;  //离散化前的排序,若数字相同把更早输入的放在前面,这样在可以避免相同数字作为逆序对的情况
}
void add (ll i, ll x)
{
	for(; i <= n; i += lowbit(i))
	{
		t[i] += x;
	}
}
ll getsum(ll i)
{
	ll ans = 0;
	for(; i; i -= lowbit(i))
	{
		ans += t[i];
	}
	return ans;
}
int main ()
{	
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	For(i, 1, n)
	{
		cin >> a[i].val; 
		a[i].id = i;
	}
	sort(a + 1, a + 1 + n, cmp);
	For(i, 1, n)
		rankk[a[i].id] = i; //离散化,第a[i].id个输入的数字第i大
	ll ans = 0;
	For(i, 1, n)
	{
		add(rankk[i], 1);
		ans += getsum(rankk[i]) - 1; // ans += getsum(rankk[i] - 1); 两种都可以
	}
	cout << ans << endl;
	return 0;
}	

利用桶排思想,遍历每个数字时,只需看看比他大的数字有多少个即可

由此,我们可以通过利用树状数组维护这个计数的数组,t[i]表示第i大的数字的个数

根据最开始输入的顺序遍历,对于原数组第i个数,我们只需求树状数组1-(第i个数的排名-1)的前缀和,即可知道在遍历到第i个数时,比他大的数字有多少个

离散化防止树状数组炸掉

离散化前的排序,若数字相同把更早输入的放在前面,这样在可以避免相同数字作为逆序对的情况,因为在遍历到相同数字序号更小的时候,序号更大的数字还未遍历到并计数进树状数组,而在遍历到相同数字序号更大的时候,序号更小的数字排名更小,所以不会加入所求前缀和

posted @ 2021-11-04 20:16  Yra  阅读(43)  评论(0)    收藏  举报