,先用数组数组维护一个c【x】 函数用于记录对应集合[l,r] 范围内数的个数,接着倒叙扫描给定1序列 a ,
(1)每次查询前缀和 【1 , a[i] - 1】 累加到答案之中。
(2)执行 “单点增加操作” 把 a[i]上的数加一 ,可以理解为扫描这个数之后,开始扫描这个数之前的数,那么就要更新一下数状数组了。
由于是倒叙开点,那么当前形成的树状数组中,的值都是在这个数之后出现的数,所以查询【1 , a[i] - 1】,即可找到关于这个数的逆序对数。
#include<bits/stdc++.h>
using namespace std;
int c[10010];
int t[10010]; //维护范围内的数
int a[10];
const int N = 100;
int ask(int x)
{
int ans = 0;
for(; x ; x -= (x & - x)) ans += c[x];
return ans ;
}
void add(int x,int y)
{
for(; x <= N; x+= x & - x) c[x] += y;
}
int main()
{
int ans = 0;
int n ;
cin >> n;
for(int i = 1 ;i <= n ; i++) cin >> a[i];
for (int i = n; i;i--)
{
ans += ask(a[i] - 1);
add(a[i] , 1);
}
cout << ans << endl;
}
由此,我们也知道建立树状数组的顺序不同,也会产生影响。
(2)A Tiny Problem with intergers
由于题目要求的是区间修改,那么一一进行修改势必复杂度偏大,这时候可以维护一个树状数组b, 类似于差分思想 ,在 b[l] + d , 在 b[r+1] - d;这样查询b 的前缀和 的时候,就可以知道b 这个数 被增加了 几次 。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int c[N];
int t[N]; //维护范围内的数
int a[N];
//单点查询,区间修改,改单点修改,区间查询。
int ask(int x)
{
int ans = 0;
for(; x ; x -= (x & - x)) ans += c[x];
return ans ;
}
void add(int x,int y)
{
for(; x <= N; x+= x & - x) c[x] += y;
}
int main()
{
int n , x;
cin >> n >> x ;
char op;
int aq;
for(int i = 1 ; i <= n ; i++) cin >> a[i];
for(int i = 1 ; i <= x ; i++)
{
cin >> op;
if(op == 'Q')
{
cin >> aq;
cout << a[aq] + ask(aq) << endl;
}
else
{
int l,r,d;
cin >> l >> r >> d;
add(l,d);
add(r+1,-1*d);
}
}
}
(3) 用树状数组确定范围时,对于给定的n取 t=log2(n),然后从最高次幂开始枚举,对于小于数的2的次幂采用这种思想,可以确切找出数。