P3374 【模板】樹狀數組 1 題解

博客園同步

原題鏈接

給定一個長度爲 nn 的數組,qq 組操作:

  • 將某一個數加上 xx

  • 求出某區間每一個數的和

顯然,假設你現在什麼也不會。

我們只考慮第 22 個操作,即先不考慮修改,如何處理區間和的詢問?

顯然,對於初始的數組 aia_i,只需要做一個前綴和 ss 使得:

sj=i=1jais_j = \sum_{i=1}^j a_i

這樣,對於一組詢問:

i=lrai=srsl1\sum_{i=l}^r a_i = s_r - s_{l-1}

可以做到 O(n)O(1)\mathcal{O}(n) - \mathcal{O}(1).

但是現在出題人加入了修改操作,你再修改的時候不得不把 ss 重推一遍。

這樣 O(n)O(n)\mathcal{O}(n) - \mathcal{O}(n) 我們就完了啊。

從前綴和上我們可以考慮,如何用 較少的前綴和記錄較多的區間答案

一個合法的思路是利用 RMQ\text{RMQ},記:

fx,y=i=xmin(x+2y1,n)aif_{x,y} = \sum_{i=x}^{\min(x+2^y-1 , n)} a_i

然後可以做到 O(nlogn)O(1)\mathcal{O}(n \log n) - \mathcal{O}(1).

但是我們覺得這不好,我們用部分前綴和。

即我們只記錄一部分的前綴和,查詢時查詢較少一部分,修改時也只要修改較少一部分,達到複雜度的均衡。

因此,我們想到了用 lowbit\text{lowbit} 表示前綴的長度。你可以理解爲每次 ++- 一個 lowbit\text{lowbit} 會在原數的二進制中砍掉一位。

這樣,我們就可以做到 O(nlogn)O(logn)\mathcal{O}(n \log n) - \mathcal{O}(\log n).

這就是樹狀數組。其實樹狀數組的本質就是用 部分前綴和維護差分數組

時間複雜度:O(nlogn)O(logn)\mathcal{O}(n \log n) - \mathcal{O}(\log n).

實際得分:100pts100pts

#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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章