2018ICPC沈阳网络赛 - J. Ka Chang

题意:

给定一棵结点数为 n 的有根树,1 号结点为根,深度为 0,每个结点初始权值为 0。现有 q 个操作,①:1 L X,将深度为 L 的结点权值加上 X;②:2 X,询问以 X 为根的子树权值和。(n, q <= 1e5)

链接:

https://nanti.jisuanke.com/t/A1998

解题思路:

子树查询,转化为dfs序区间查询,主要在于修改操作。考虑分块,对于结点数不超过 n \sqrt{n} n 的层,直接暴力修改;否则,打上标记,询问时对标记层二分落在dfs序区间的结点区间。由于结点数大于 n \sqrt{n} n 的层不超过 n \sqrt{n} n ,修改和查询复杂度都是O( n \sqrt{n} n logn)。

参考代码:
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int maxm = 5e2 + 5;
const int mod = 998244353;
const int inf = 0x3f3f3f3f;

vector<int> G[maxn], P[maxn], B;
int in[maxn], out[maxn], rk[maxn], dep[maxn];
ll c[maxn], add[maxn];
int n, q, tim;

void dfs(int u){

	in[u] = ++tim, rk[tim] = u;
	for(int i = 0; i < sz(G[u]); ++i){

		int v = G[u][i];
		dep[v] = dep[u] + 1;
		dfs(v);
	}
	out[u] = tim;
}

#define lowb(x) ((x)&(-x))
void update(int x, int v){

	while(x <= n) c[x] += v, x += lowb(x);
}

ll query(int x){

	ll ret = 0;
	while(x) ret += c[x], x -= lowb(x);
	return ret;
}

int main(){

	scanf("%d%d", &n, &q);
	for(int i = 1; i < n; ++i){

		int u, v; scanf("%d%d", &u, &v);
		G[u].pb(v);
	}
	dfs(1);
	for(int i = 1; i <= n; ++i){

		P[dep[rk[i]]].pb(i);
	}
	int len = sqrt(n);
	for(int i = 0; i < n; ++i){

		if(sz(P[i]) > len) B.pb(i);
	}
	while(q--){

		int opt, x, y; scanf("%d%d", &opt, &x);
		if(opt == 1){

			scanf("%d", &y);
			if(sz(P[x]) <= len){

				for(int i = 0; i < sz(P[x]); ++i) update(P[x][i], y);
			}
			else add[x] += y;
		}
		else{

			ll ret = query(out[x]) - query(in[x] - 1);
			for(int i = 0; i < sz(B); ++i){

				if(!add[B[i]]) continue;
				int l = lower_bound(P[B[i]].begin(), P[B[i]].end(), in[x]) - P[B[i]].begin();
				int r = lower_bound(P[B[i]].begin(), P[B[i]].end(), out[x] + 1) - P[B[i]].begin();
				if(l >= r) continue;
				ret += add[B[i]] * (r - l);
			}
			printf("%lld\n", ret);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值