可以先看看这位博主的介绍
可以用update(O(logn))初始化,也可以用init(O(n))初始化。
单点修改 + 区间查询
1:
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const double eps = 1e-5;
const int mod = 2013;
const int N = 5e5+10;
int c[N];
int n,m,a;
int lowbit(int x){ return x & -x;}
void update(int x,int y){
for(int i=x;i<=n;i+=lowbit(i)) c[i] += y;
}
void init(int x,int b){
int y = x; x = lowbit(x) >> 1;
c[y] += b;
while(x){
c[y] += c[y-x];
x >>= 1;
}
}
int ask(int x){
int sum = 0;
for(int i=x;i;i-=lowbit(i)) sum += c[i];
return sum;
}
signed main(){
IOS
#ifdef ddgo
freopen("C:\\Users\\asus\\Desktop\\ddgoin.txt","r",stdin);
#endif
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a,init(i,a);//update(i,a);
while(m --){
int k,x,y; cin>>k>>x>>y;
if(k == 1) update(x,y);
else cout<<ask(y)-ask(x-1)<<endl;
}
return 0;
}
例题
这里有一道树状数组与逆序对得例题。
题目地址
因为我们维护的是前缀和,从后往前判断比较方便。
对于一个数,只要我们找到它后面有x个比它小的,那么答案 += x.
处理过程
1: 询问当前的 1~a[i-1]的和,表示在当前这个数之前,已经出现了比这个数小的个数。
2: 当前询问完后,对其a[x]位置进行一个 +1 的维护,表示这个数出现次数+1.
3: 记住离散化
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const double eps = 1e-5;
const int mod = 2013;
const int N = 1e6+10;
int c[N],a[N],b[N];
int n,m;
int lowbit(int x){ return x & -x;}
void update(int x,int y){
for(int i=x;i<=n;i+=lowbit(i)) c[i] += y;
}
int ask(int x){
int sum = 0;
for(int i=x;i;i-=lowbit(i)) sum += c[i];
return sum;
}
int find(int x){
return lower_bound(b+1,b+m,x)-b;
}
signed main(){
IOS
#ifdef ddgo
freopen("C:\\Users\\asus\\Desktop\\ddgoin.txt","r",stdin);
#endif
cin>>n;
int res = 0;
for(int i=1;i<=n;i++) cin>>a[i],b[i] = a[i];
sort(b+1,b+n+1);
m = unique(b+1,b+n+1)-b;
for(int i=n;i;i--){
int t = find(a[i]);
res += ask(t-1);
update(t,1);
}
cout<<res<<endl;
return 0;
}
区间修改 + 单点查询
2:
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const double eps = 1e-5;
const int mod = 2013;
const int N = 5e5+10;
int c[N];
int n,m,a;
int lowbit(int x){ return x & -x;}
void update(int x,int y){
for(int i=x;i<=n;i+=lowbit(i)) c[i] += y;
}
int ask(int x){
int sum = 0;
for(int i=x;i;i-=lowbit(i)) sum += c[i];
return sum;
}
signed main(){
IOS
#ifdef ddgo
freopen("C:\\Users\\asus\\Desktop\\ddgoin.txt","r",stdin);
#endif
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a,update(i,a),update(i+1,-a);
while(m --){
int k,x,y,z; cin>>k;
if(k == 1){
cin>>x>>y>>z;
update(x,z);
update(y+1,-z);
}
else{
cin>>x;
cout<<ask(x)<<endl;
}
}
return 0;
}
区间修改 + 区间查值
用差分思想 求区间的值我们得到
∑
i
=
1
p
∑
j
=
1
i
c
[
i
]
\sum_{i=1}^{p}\sum_{j=1}^{i}c[i]
∑i=1p∑j=1ic[i]
可以知道
∑
i
=
1
p
c
[
i
]
∗
(
p
−
i
+
1
)
\sum_{i=1}^{p}c[i]*(p-i+1)
∑i=1pc[i]∗(p−i+1)
再化简得
(
p
+
1
)
∗
∑
i
=
1
p
c
[
i
]
(p+1)*\sum_{i=1}^{p}c[i]
(p+1)∗∑i=1pc[i] -
∑
i
=
1
p
c
[
i
]
∗
i
\sum_{i=1}^{p}c[i]*i
∑i=1pc[i]∗i
所以我们多维护一个c2[i] 存放 c[i]*i。
3:
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const double eps = 1e-5;
const int mod = 2013;
const int N = 1e6+10;
int c1[N],c2[N];
int n,m,a;
int lowbit(int x){ return x & -x;}
void update(int x,int y){
for(int i=x;i<=n;i+=lowbit(i)) c1[i] += y,c2[i] += x*y;
}
int ask(int x){
int sum = 0;
for(int i=x;i;i-=lowbit(i)) sum += (x + 1)*c1[i] - c2[i];
return sum;
}
signed main(){
IOS
#ifdef ddgo
freopen("C:\\Users\\asus\\Desktop\\ddgoin.txt","r",stdin);
#endif
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a,update(i,a),update(i+1,-a);
while(m --){
int k,x,y,z; cin>>k;
if(k == 1){
cin>>x>>y>>z;
update(x,z);
update(y+1,-z);
}
else{
cin>>x>>y;
cout<<ask(y)-ask(x-1)<<endl;
}
}
return 0;
}