题意: 给出一颗以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;
}