树状数组学习笔记
树状数组,可以用于处理前缀和,前缀积,前缀最大值等等
相比于直接处理前缀和,优势在于修改和查询都是logn的,但是功能有限
树状数组通过将每一组数分为多块,提前处理好部分的和,在求值时根据特定的路径去查询
注意由于树状数组使用lowbit,所以不能直接处理0相关的问题
区间处理
在进行区间查询时,如果具有可逆性,那么可以直接将从r的查询结果和l-1的进行逆操作
但是对于不能逆操作的,比如求最大值,通常需要两个log,不如线段树
在进行区间加法时,要同时给一个区间加一个数,可以有两种方法
第一种,单点查询,只用在树状数组中装每两个之间的差值即可
然而,如果要区间查询,则需要使用区间修改法
具体方法是设两个数组,修改时代码:
void add(int k, int v) {
int v1 = k * v;
while (k <= n) {
t1[k] += v, t2[k] += v1;
// 注意不能写成 t2[k] += k * v,因为 k 的值已经不是原数组的下标了
k += lowbit(k);
}
}
查询时代码:
long long getsum1(int l, int r) {
return (r + 1ll) * getsum(t1, r) - 1ll * l * getsum(t1, l - 1) -
(getsum(t2, r) - getsum(t2, l - 1));
}
其余部分找常即可
第k小值
首先将所有数输入进来,进行离散化
第一种方法是二分第k小值,然后在树状数组中进行查找,两个log
然鹅还要方法可以在一个log内完成
考虑倍增的思想,由于树状数组的性质,可以倍增实现
代码:
// 权值树状数组查询第 k 小
int kth(int k) {
int sum = 0, x = 0;
for (int i = log2(n); ~i; --i) {
x += 1 << i; // 尝试扩展
if (x >= n || sum + t[x] >= k) // 如果扩展失败
x -= 1 << i;
else
sum += t[x];
}
return x + 1;
}
二维偏序问题
使用树状数组,可以很方便地处理二维偏序问题
由于存在两个参考量,可以先将其中一条安装加入时间的顺序进行排序,然后通过树状数组按照前缀后缀的顺序查找,即可得出符合两个条件的其他项的数量了
常见的问题包括逆序对,由于需要按照初步排列的顺序,加入时间就为从后往前,这边处理是每一个逆序对找后面的一项
然后加权时直接将值离散化后的结果加入,然后即可处理出来
#include<bits/stdc++.h>
using namespace std;
int n,a[100005],b[100005];
int lowbit(int x){
return x&-x;
}
struct BT{
int c[100005];
void add(int x,int y){
for(int i=x;i<=n;i+=lowbit(i))c[i]+=y;
}
int query(int x){
int ans=0;
for(int i=x;i>=1;i-=lowbit(i))ans+=c[i];
return ans;
}
}bit;
signed main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)b[i]=a[i];
sort(b+1,b+n+1);
unique(b+1,b+n+1);
for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+n+1,a[i])-b;
int ans=0;
for(int i=n;i>=1;i--){
ans+=bit.query(a[i]-1);
bit.add(a[i],1);
}
cout<<ans;
return 0;
}
树状数组的离线处理
在问题是类似于某个点只出现一次时,由于每个点在范围不同时贡献不同,但是不能每次都进行预处理,否则必定会超时
考虑能否直接沿用上之前预处理后的结果
因此在这类题目中,通常含有r和l两个值,安装r处理完成以后,再通过l在树状数组中得到答案即可
示例代码丹钓战
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q,cnt,pre[500005],a[500005],b[500005],ans[500005];
struct P{
int a,b,id;
}p[500005];
struct Q{
int l,r,id;
}c[500005];
bool cmp(Q a,Q b){
return a.r<b.r;
}
int lowbit(int x){
return x&-x;
}
struct BT{
int c[500005];
void init(){
memset(c,0,sizeof(c));
}
void add(int x,int y){
x++;
for(int i=x;i<=n;i+=lowbit(i))c[i]+=y;
}
int query(int x){
x++;
int ans=0;
for(int i=x;i>=1;i-=lowbit(i))ans+=c[i];
return ans;
}
int query(int l,int r){
return query(r)-query(l-1);
}
}bit;
signed main(){
scanf("%lld%lld",&n,&q);
for(int i=1;i<=n;i++)scanf("%lld",a+i);
for(int i=1;i<=n;i++)scanf("%lld",b+i);
for(int i=1;i<=n;i++){
while(cnt!=0&&!(p[cnt].a!=a[i]&&p[cnt].b>b[i])){
cnt--;
}
if(!cnt)pre[i]=0;
else pre[i]=p[cnt].id+1;
p[++cnt]={a[i],b[i],i};
}
for(int i=1;i<=q;i++){
scanf("%lld%lld",&c[i].l,&c[i].r);
c[i].id=i;
}
sort(c+1,c+q+1,cmp);
int w=0;
for(int i=1;i<=q;i++){
while(w<c[i].r){
w++;
bit.add(pre[w],1);
}
ans[c[i].id]=bit.query(c[i].l)-c[i].l+1;
}
for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
return 0;
}

浙公网安备 33010602011771号