Codeforces Gym 100589A Queries on the Tree(树状数组+分块)



题意: 给出一颗以1为根的树, 初始的时候每个节点上的硬币数量都是0, 树的结点个数为N <= 100000, 接下来是M <= 10000次操作, 每次操作要么是将所有深度为L的结点上的硬币数量增加一个值, 要么是询问以x为根的子树上的所有节点的硬币数量之和。

思路:树状数组+分块。因为要输出的是以x为根的子树的结点和,所以不难想到将树转成线性结构,然后用树状数组维护前缀和。但是这样有一个问题,就是当某一深度节点数量太多时,单次修改的复杂度可能达到n*logn无法承受。还有另外一种修改方式是记录每个深度被修改的值的和,然后对于询问,求出当前子树每个深度的节点数量然后乘以修改的值的和,但是这样做的话当树退化成一条链的时候复杂度还是n*logn。考虑用分块的思想结合这两种方式

,当某一深度的节点数小于sqrt(n)时,直接暴力树状数组修改,当某一深度的节点数大于等于sqrt(n)时,记录下这一深度,然后用第二种方式修改,因为节点数大于等于sqrt(n)的深度不会超过sqrt(n)个,所以最坏时间复杂度是O((sqrt*N)*M*logN))。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 100100;
const int limit = (int)sqrt(100000);
int n, m;
int dfs_clock = 0, tim[MAXN][2], maxd;
vector<int> G[MAXN], dept[MAXN], big;
LL cnt[MAXN], c[MAXN];
int lowbit(int x) {
	return x & -x;
}
void update(int x, int v) {
	while(x <= n) {
		c[x] += v;
		x += lowbit(x);
	}
}
LL sumv(int x) {
	LL ret = 0;
	while(x) {
		ret += c[x];
		x -= lowbit(x);
	}
	return ret;
}
void dfs(int cur, int fa, int dep) {
	maxd = max(maxd, dep);
	tim[cur][0] = ++dfs_clock;
	dept[dep].push_back(tim[cur][0]);
	for(int i = 0; i < G[cur].size(); i++) {
		int u = G[cur][i];
		if(u == fa) continue;
		dfs(u, cur, dep+1);
	}
	tim[cur][1] = dfs_clock;
} 
int main() {
    //freopen("input.txt", "r", stdin);
	cin >> n >> m;
	for(int i = 1, u, v; i < n; i++) {
		scanf("%d%d", &u, &v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1, 0, 0);
	for(int i = 1; i <= maxd; i++) {
		if(dept[i].size() >= limit) {
			big.push_back(i);
			sort(dept[i].begin(), dept[i].end());
		}
	}
	for(int i = 1, op, x, y; i <= m; i++) {
		scanf("%d", &op);
		if(op == 1) {
			scanf("%d%d", &x, &y);
			int sz = dept[x].size();
			if(sz < limit) {
				for(int j = 0; j < sz; j++) update(dept[x][j], y);
			}
			else cnt[x] += y;
		}
		else {
			int x; scanf("%d", &x);
			int l = tim[x][0], r = tim[x][1];
			LL ans = sumv(r) - sumv(l-1);
			int sz = big.size();
			for(int j = 0; j < sz; j++) {
				int d = big[j];
				ans += (upper_bound(dept[d].begin(), dept[d].end(), r)-lower_bound(dept[d].begin(), dept[d].end(), l))*cnt[d];
			}
			printf("%I64d\n", ans);
		}
	}
    return 0;
}


















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
树状数组(Fenwick Tree)是一种用于高效处理区间和查询的数据结构,常用于解一维数组的前缀和、区间更新和查询等问题。 在 Codeforces 上,树状数组常被用来解决一些与区间和查询有关的问题。它可以在 O(logn) 的时间内完成单点更新和查询,以及区间求和等操作。 下面是一个简单的示例代码,展示了如何实现一个基本的树状数组: ```cpp #include <iostream> #include <vector> using namespace std; // 获取最低位的 1 int getLowbit(int x) { return x & -x; } // 树状数组的单点更新操作 void update(vector<int>& fenwick, int index, int delta) { while (index < fenwick.size()) { fenwick[index] += delta; index += getLowbit(index); } } // 树状数组的前缀和查询操作 int query(vector<int>& fenwick, int index) { int sum = 0; while (index > 0) { sum += fenwick[index]; index -= getLowbit(index); } return sum; } int main() { int n; cin >> n; vector<int> fenwick(n + 1, 0); // 初始化树状数组 for (int i = 1; i <= n; i++) { int val; cin >> val; update(fenwick, i, val); } // 进行查询操作 int q; cin >> q; while (q--) { int type; cin >> type; if (type == 1) { int index, delta; cin >> index >> delta; update(fenwick, index, delta); } else if (type == 2) { int l, r; cin >> l >> r; int sum = query(fenwick, r) - query(fenwick, l - 1); cout << sum << endl; } } return 0; } ``` 在这个示例中,我们使用了一个长度为 n 的数组 `fenwick` 来表示树状数组。`update` 函数用于更新树状数组中的某个元素,`query` 函数用于查询树状数组中某个区间的和。 你可以根据具体问题的要求进行相应的修改和扩展。希望对你有所帮助!如果有任何疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值