刍议树状数组
树状数组
用处
区间加,单点查询
单点加,区间查询
区间加,区间查询
求逆序对
……
思想
树状数组的思想对于线段树等结构来说比较抽象,所以我也懒得讲……
在这我只讲一下我对于树组的理解,对于实战来说完全够用。
先讲一个叫
然后在树状数组里我们定义一个
然后你可以这样理解: 对于二进制
有些抽象,还是看代码吧……
树状数组支持的操作有两种
int ask(int x){ int ans=0; while(x<=n){ ans+=c[x]; x+=lowbit(x); } return ans; }
int add(x,k){ while(x){ c[x]+=k; x-=lowbit(x); } }
求逆序对
用
逆序对条件
So 从右往左遍历数组
步骤:
代码
for(int i=n;i;i--){ ans+=ask(a[i]-1); add(a[i],1); }
复杂度为
当然了,当数据范围较大时,要先离散化,本生就要排序,所以在数据范围较大时不如直接用归并排序。
例题:
https://www.acwing.com/problem/content/description/243/
区间修改,单点查询
用差分思想,树状数组维护一个差分数组数组
操作时
add(l,d);
add(r+1,-d);
就行了。
例题:
https://www.luogu.com.cn/problem/P3368
区间修改,区间查询
修改操作思路不变,用差分数组
emm……下面太难写了,拉一段李煜东大哥的书吧
在本题中,我们增加一个树状数组,用于维护
具体来说,我们建立两个树状数组
然后……
步骤:
1.在树状数组
2.在树状数组
3.在树状数组
4.在树状数组
另外,我们建立数组
sum[r]+(r+1)*ask(0,r)-ask(1,r) - sum[l-1]+l*ask(0,l-1)-ask(1,l-1);
例题:
https://www.acwing.com/problem/content/description/244/
大家应该不会偷懒去写线段树吧……
代码
#include<bits/stdc++.h> using namespace std; #define int long long int n,m; const int N=1e5+5; int a[N],sum[N],c[2][N];//c[0]->b[i]c[1]->i*b[i] #define FOR(i,_l,_r) for(int i=_l;i<=_r;i++) int lowbit(int x){ return x&-x; } void add(int x,int id,int k){ while(x<=n){ c[id][x]+=k; x+=lowbit(x); } } int ask(int id,int x){ int ans=0; while(x){ ans+=c[id][x]; x-=lowbit(x); } return ans; } signed main() { ios::sync_with_stdio(false); cin.tie(NULL);cout.tie(NULL); cin>>n>>m; FOR(i,1,n){ cin>>a[i]; sum[i]=sum[i-1]+a[i]; } while(m--){ char opt; int l,r,d; cin>>opt>>l>>r; if(opt=='C'){ cin>>d; add(l,0,d); add(r+1,0,-d); add(l,1,l*d); add(r+1,1,-(r+1)*d); } else{ int ans=sum[r]+(r+1)*ask(0,r)-ask(1,r); ans-=sum[l-1]+l*ask(0,l-1)-ask(1,l-1); cout<<ans<<endl; } } return 0; }
作业:
https://www.acwing.com/problem/content/description/245/
作业提示:二分答案……
完结撒花
喜欢的点个赞,感激不尽