树状数组
解决的问题
- 基本问题:单点修改,区间查询
- 利用差分:区间修改,区间查询
总的来说就是:频繁修改+区间查询
与线段树区别
树状数组可以解决的问题都可以用线段树解决。
两者的区别
树状数组的优点:
- 相比线段树系数系数要少很多
- 容易写,代码量小
线段树的优点:
- 可以解决复杂问题。
理解
代码
lowbit
int lowbit(int x) {return x & (-x);}
更新/建立
一般来说建立就是直接将树状数组更新n次
//x为更新的位置,y为更新后的数,n为数组最大值 void update(int x,int y,int n){ for(int i=x;i<=n;i+=lowbit(i)) c[i] += y; }
查询
查询1~x之间的数的和。
很明显查询一段区间的就是sum(x)-sum(y)
int sum(int x){ int ans = 0; for(int i=x;i;i-=lowbit(i)) ans += c[i]; return ans; }
应用
单点修改,区间查询
思路:
对于某个点a[i]的V
的数量就是:
a[i]前面大于a[i]的数的数量 × a[i]后面大于a[i]的数的数量
∧
也同理,就是换成小于a[i]的数的 数量
#include <bits/stdc++.h> #define endl '\n' #define int long long using namespace std; typedef pair<int, int> pii; const int N = 2000010; const int INF = 0x3f3f3f3f3f; int n; int a[N]; int tr[N]; int upper[N],lower[N]; int lowbit(int x){ return x & -x; } void add(int x,int c){ for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c; } int sum(int x){ int res=0; for(int i=x;i;i-=lowbit(i)) res+=tr[i]; return res; } void slove() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); //统计i前面 大于和小于 i的数 的数量 for(int i=1;i<=n;i++){ int x=a[i]; upper[i]=sum(n)-sum(x); lower[i]=sum(x-1); add(x,1); } memset(tr,0,sizeof tr); int ans1=0,ans2=0; //统计i后面 大于和小于 i的数 的数量,并求出答案 for(int i=n;i>=1;i--){ int y=a[i]; ans1+=upper[i]*(sum(n)-sum(y)); ans2+=lower[i]*sum(y-1); add(y,1); } printf("%lld %lld\n",ans1,ans2); } signed main() { slove(); return 0; }
区间修改,单点查询
242. 一个简单的整数问题
题意:
有两类指令:
- 第一类指令形如 C l r d,表示把数列中第 l∼r 个数都加 d。
- 第二类指令形如 Q x,表示询问数列中第 x 个数的值。
思路: 裸题, 利用树状数组维护差分数组 即可,看代码吧
#include <bits/stdc++.h> #define endl '\n' #define int long long using namespace std; typedef pair<int, int> pii; const int N = 1e5+10; const int INF = 0x3f3f3f3f3f; int n,m; int a[N]; int tr[N]; int lowbit(int x){ return x & -x; } void add(int x,int c){ for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c; } int sum(int x){ int res=0; for(int i=x;i;i-=lowbit(i)) res+=tr[i]; return res; } void slove() { cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; } while(m--){ string s;cin>>s; if(s=="Q"){ int x;cin>>x; cout<<sum(x)+a[x]<<endl; } else{ int l,r,d; cin>>l>>r>>d; add(l,d); add(r+1,-d); } } } signed main() { slove(); return 0; }
区间修改,区间查询
维护两个树状数组即可:
- 一个是差分数组d[i]的树状数组tr[i]
- 一个是原始数组的差分数组乘以i即i*d[i]的树状数组tri[i]
#include <bits/stdc++.h> #define endl '\n' #define int long long using namespace std; typedef pair<int, int> pii; const int N = 1e5 + 10; const int INF = 0x3f3f3f3f3f; int n, m; int a[N]; int tr[N]; int tr2[N]; int lowbit(int x) { return x & -x; } void add(int x, int c) { for (int i = x; i <= n; i += lowbit(i)) { tr[i] += c; tr2[i] += x * c; } } int sum(int x) { int res = 0; for (int i = x; i; i -= lowbit(i)) res += (x + 1) * tr[i] - tr2[i]; return res; } void slove() { cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> a[i]; add(i,a[i]-a[i-1]); } while (m--) { string s; cin >> s; if (s == "Q") { int l, r; cin >> l >> r; cout << sum(r) - sum(l - 1) << endl; } else { int l, r, d; cin >> l >> r >> d; add(l, d); add(r + 1, -d); } } } signed main() { slove(); return 0; }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/15826275.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步