树状数组浅析
树状数组
树状数组可以将一整个区间划分为几个区间,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个数时,比他大的数字有多少个
离散化防止树状数组炸掉
离散化前的排序,若数字相同把更早输入的放在前面,这样在可以避免相同数字作为逆序对的情况,因为在遍历到相同数字序号更小的时候,序号更大的数字还未遍历到并计数进树状数组,而在遍历到相同数字序号更大的时候,序号更小的数字排名更小,所以不会加入所求前缀和

浙公网安备 33010602011771号