树状数组学习笔记

一种可以单点修改,查询前缀和的数据结构。
每次操作O(logn)

void update(int x,int k) {
	for(;x <= n;x += x & (-x)) t[x]+=k;
}
int query(int x) {
	int res=0;
	for(;x;x -= x & (-x)) res+=t[x];
	return res;
}

例题一:P1966 [NOIP2013 提高组] 火柴排队

思路:离散化数组,排序后a[i].id,b[i].id表示大小第i位的数在原数组的位置。

q[a[i].id] = b[i].id表示a数组排名第i位的数的原位置对应b数组排名第i位的数的原位置,当q[i] = i时即为答案。故求q数组的逆序对即可(树状数组)

AC代码

 #include <bits/stdc++.h>

using namespace std;
const int MOD = 1e8 - 3;
const int N = 1e5 + 10;
struct node{
	int id,val;
}a[N],b[N];
int ans,n,g[N << 2],q[N];
bool cmp(node x,node y) {
	if(x.val == y.val) return x.id < y.id;
	return x.val < y.val;
}
void add(int i,int x){
	for(;i <= n;i += i & (-i)) g[i] += x;
}
int getsum(int i){
	int res = 0;
	for(;i;i -= i & (-i)) res += g[i];
	return res; 
}
int main(){
	cin >> n;
	for(int i = 1;i <= n;i++){
		cin >> a[i].val;
		a[i].id = i;
	}
	for(int i = 1;i <= n;i++){
		cin >> b[i].val;
		b[i].id = i;
	}
	sort(a + 1,a + 1 + n,cmp);
	sort(b + 1,b + 1 + n,cmp);
	for(int i = 1;i <= n;i++){
		q[a[i].id] = b[i].id;
	}
	for(int i = 1;i <= n;i++){
		add(q[i],1);
		ans += i - getsum(q[i]);
		ans %= MOD;
	}
	cout << ans;
	return 0;
} 

例题二:P3605 [USACO17JAN] Promotion Counting P

思路:跟上一题基本类似,离散化找逆序对即可,只不过变成了在树上

AC代码

 #include <bits/stdc++.h>

using namespace std;
const int N = 3e5 + 10;
int n,q[N],head[N << 1],cnt,ans[N],g[N];
struct node{
	int id,val;
}a[N];
bool cmp(node x,node y){
	return x.val < y.val;
}
struct edge{
	int to,next;
}e[N << 1];
void add_edge(int u,int v){
	e[++cnt].to = v;
	e[cnt].next = head[u];
	head[u] = cnt;
}
void add(int i,int x){
	for(;i <= n;i += i & (-i)) g[i] += x; 
}
int query(int i){
	int res = 0;
	for(;i;i -= i & (-i)) res += g[i];
	return res; 
}
void dfs(int u){
	ans[u] -= query(n) - query(q[u]);
	for(int i = head[u];i;i = e[i].next){
		dfs(e[i].to);
	}
	ans[u] += query(n) - query(q[u]);
	add(q[u],1);
}
int main(){
	cin >> n;
	for(int i = 1;i <= n;i++){
		cin >> a[i].val;
		a[i].id = i;
	}
	sort(a + 1,a + n + 1,cmp);
	for(int i = 1;i <= n;i++){
		q[a[i].id] = i;
	}
	for(int i = 2;i <= n;i++){
		int x;
		cin >> x;
		add_edge(x,i);
	}
	dfs(1);
	for(int i = 1;i <= n;i++) cout << ans[i] << endl;
	return 0;
} 

例题三:P1972 [SDOI2009] HH的项链

思路:离线树状数组,考虑当我们碰到一个元素如果已经出现过了,那么就将之前出现的那个位置用数状数组-1,否则+1即可

AC代码

 #include <bits/stdc++.h>

using namespace std;
const int N = 1e6 + 10;
int n,m,a[N],pre[N],g[N << 2],ans[N];
struct node{
	int l,r,id;
}q[N];
bool cmp(node x,node y){
	return x.r < y.r;
}
void add(int i,int x){
	for(;i <= n;i += i & (-i)) g[i] += x;
}
int query(int i){
	int res = 0;
	for(;i;i -= i & (-i)) res += g[i];
	return res;
}
int main(){
	cin >> n;
	for(int i = 1;i <= n;i++) cin >> a[i];
	cin >> m;
	for(int i = 1;i <= m;i++){
		cin >> q[i].l >> q[i].r;
		q[i].id = i;
	}
	sort(q + 1,q + m + 1,cmp);
	for(int i = 1,k = 1;i <= m;i++){
		for(int j = k;j <= q[i].r;j++){
			if(pre[a[j]]) add(pre[a[j]],-1);
			pre[a[j]] = j;
			add(j,1);
		}
		k = q[i].r + 1;
		ans[q[i].id] = query(q[i].r) - query(q[i].l - 1);
	}
	for(int i = 1;i <= m;i++){
		cout << ans[i] << endl;
	}
	return 0;
} 

例题四:P4113 [HEOI2012] 采花

思路:和上一题一样,只是加了一个限制条件

AC代码

 #include <bits/stdc++.h>

using namespace std;
const int N = 2e6 + 10;
int n,c,m,a[N],pre[3][N],g[N << 2],ans[N];
struct node{
	int l,r,id;
}f[N];
bool cmp(node x,node y){
	return x.r < y.r;
}
void add(int i,int x){
	for(;i <= n;i += i & (-i)) g[i] += x; 
}
int query(int i){
	int res = 0;
	for(;i;i -= i & (-i)) res += g[i];
	return res;
} 
int main(){
	cin >> n >> c >> m;
	for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
	for(int i = 1;i <= m;i++){
		scanf("%d%d",&f[i].l,&f[i].r);
       	f[i].id=i;
	}
	sort(f + 1,f + m + 1,cmp);
	for(int i = 1,k = 1;i <= m;i++){
		for(int j = k;j <= f[i].r;j++){
			if(pre[1][a[j]]){
				if(pre[2][a[j]]){
					add(pre[2][a[j]],-1);
				}
				add(pre[1][a[j]],1);
			}
			pre[2][a[j]] = pre[1][a[j]];
			pre[1][a[j]] = j;
		}
		k = f[i].r + 1;
		ans[f[i].id] = query(f[i].r) - query(f[i].l - 1);
	}
	for(int i = 1;i <= m;i++) cout << ans[i] << endl;
	return 0;
}

例题五:P4054 [JSOI2009] 计数问题

思路:二维树状数组跟二维前缀和差不多

AC代码
 #include <bits/stdc++.h>

using namespace std;
const int N = 310;
int n,m,g[N][N][110],a[N][N],q;
void add(int x,int y,int k,int c){
	for(int i = x;i <= n;i += i & (-i)){
		for(int j = y;j <= m;j += j & (-j)){
			g[i][j][c] += k;
		}
	}
}
int query(int x,int y,int c){
	int res = 0;
	for(int i = x;i;i -= i & (-i)){
		for(int j = y;j;j -= j & (-j)){
			res += g[i][j][c]; 
		}
	}
	return res;
}
int main(){
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m;j++){
			int c;
			cin >> c;
			a[i][j] = c;
			add(i,j,1,c);
		}
	}
	cin >> q;
	while(q--){
		int op;
		cin >> op;
		if(op == 1){
			int x,y,c;
			cin >> x >> y >> c;
			add(x,y,-1,a[x][y]);
			a[x][y] = c;
			add(x,y,1,c);
		}
		else{
			int x,y,p,q,c;
			cin >> x >> p >> y >> q >> c;
			cout << query(p,q,c) - query(x - 1,q,c) - query(p,y - 1,c) + query(x - 1,y - 1,c) << endl;
		}
	} 
	return 0;
}
posted @ 2023-10-10 21:49  LouYW07  阅读(4)  评论(0)    收藏  举报