http://www.cnblogs.com/Asm-Definer/p/9610262.html
这篇博客解释的非常好,我在这篇基础上再做解释:
树状数组的下标对应dfs序列,维护的自然是一段dfs序列的和,树状数组一次更新深度为L的T个点,每次更新的复杂度是logN,查询是查一个点,计算一段dfs序列的权值和,复杂度是logN。
利用lazy标记在更新的时候是O(1),但是在询问时需要访问所有已经有过lazy标记的层,这个层数不超过O(N/T)。
然后Q个操作就是Q(TlogN+N/T)。
这个T毫无疑问在取N/logN的时候复杂度最小,也就是Q(N+logN)。
做法太妙了,第一次接触这种题。
#include <iostream> #include <cstdio> #include <vector> #include <cmath> #include <algorithm> using namespace std; typedef long long ll; const int maxn = 1e5+10; ll tree[maxn]; ll lazy[maxn]; int st[maxn], ed[maxn]; vector<int> G[maxn]; vector<int> depth[maxn]; vector<int> layer; int timing = 0, max_dep = 0; void dfs(int u, int dep) { max_dep = max(dep,max_dep); st[u] = ++timing; depth[dep].push_back(timing); for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; dfs(v,dep+1); } ed[u] = timing; } int lowbit(int x) { return x & -x; } void add(int x, ll a) { while (x <= maxn) { tree[x] += a; x += lowbit(x); } } ll sum(int x) { ll ret = 0; while (x > 0) { ret += tree[x]; x -= lowbit(x); } return ret; } int main() { int n, q, u, v, op, l, x; long long a; scanf("%d%d",&n,&q); for (int i = 1; i < n; i++) { scanf("%d%d",&u,&v); G[u].push_back(v); } for(int i = 0; i <= n; i++) depth[i].clear(); dfs(1,0); int valve = n/log(n); for (int i = 0; i <= max_dep; i++) { if(depth[i].size() >= valve) layer.push_back(i); } for (int i = 0; i < q; i++) { scanf("%d",&op); if(op&1) { scanf("%d%lld",&l,&a); if (depth[l].size() < valve) { for (int i = 0; i < depth[l].size(); i++) { add(depth[l][i],a); } } else { lazy[l] += a; // layer.push_back(l); 为什么不能在这边更新layer?因为多次更新同一层会造成会导致计算答案时被重复计算 } } else { scanf("%d",&x); ll ans = 0; ans += sum(ed[x])-sum(st[x]-1); for (int i = 0; i < layer.size(); i++) { int deep = layer[i]; ans += lazy[deep] * (upper_bound(depth[deep].begin(), depth[deep].end(), ed[x]) - lower_bound(depth[deep].begin(), depth[deep].end(), st[x])); } printf("%lld\n",ans); } } return 0; }