給定一個長度爲 的數組, 組操作:
-
將某一個數加上
-
求出某區間每一個數的和
顯然,假設你現在什麼也不會。
我們只考慮第 個操作,即先不考慮修改,如何處理區間和的詢問?
顯然,對於初始的數組 ,只需要做一個前綴和 使得:
這樣,對於一組詢問:
可以做到 .
但是現在出題人加入了修改操作,你再修改的時候不得不把 重推一遍。
這樣 我們就完了啊。
從前綴和上我們可以考慮,如何用 較少的前綴和記錄較多的區間答案。
一個合法的思路是利用 ,記:
然後可以做到 .
但是我們覺得這不好,我們用部分前綴和。
即我們只記錄一部分的前綴和,查詢時查詢較少一部分,修改時也只要修改較少一部分,達到複雜度的均衡。
因此,我們想到了用 表示前綴的長度。你可以理解爲每次 或 一個 會在原數的二進制中砍掉一位。
這樣,我們就可以做到 .
這就是樹狀數組。其實樹狀數組的本質就是用 部分前綴和維護差分數組。
時間複雜度:.
實際得分:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+1;
typedef long long ll;
inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
int a[N]; ll c[N];
int n,m,x,y;
inline int lowbit(int x) {
//求2^{x末尾0的個數}
return x & (-x);
}
inline ll sum(int x) {
//求n的前綴和
ll s=0; while(x>0) {
s+=c[x];
x-=lowbit(x);
//這是x節點緊貼着的前一個區間
} return s;
}
inline void update(int x,int k) {
a[x]+=k; while(x<=n) {
c[x]+=k; //暴力加和
x+=lowbit(x);
//這是x節點緊貼着的後一個區間
}
}
int main(){
n=read(); m=read();
for(int i=1;i<=n;i++) {
x=read(); update(i,x);
} while(m--) {
int opt=read(); x=read(),y=read();
if(opt==1) update(x,y);
else printf("%lld\n",sum(y)-sum(x-1));
}
return 0;
}