对于一颗树,dfs遍历为每个节点标号,在进入一个树是标号和在遍历完这个树的子树后标号,那么子树所有的标号都在这两个数之间,是一个连续的区间。(好神奇~~~)
这样每次操作一个结点的子树时,在每个点的开始结束两个点标记一下就可以,用树状数组求前缀和就可知道每个点的值。
这道题虽然很麻烦(dep[y]-dep[x])%k 。但是注意到K很小(1<=k<=5),可以维护k个树状数组。
提交时编译器选GUN C++迷之RE。。。换Visual C++
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> #define clr(x,c) memset(x,c,sizeof(x)) using namespace std; typedef long long ll; const int N = 50005; vector<int> g[N]; vector<int> lev[5]; ll bit[5][N]; int d[N], l[N], r[N]; int n, m, k; int cnt; void dfs(int u, int fa, int dep) { d[u] = dep; l[u] = ++cnt; lev[dep%k].push_back(l[u]); for (unsigned i = 0; i < g[u].size(); ++i) { int v = g[u][i]; if (v == fa) continue; dfs(v, u, dep+1); } r[u] = cnt; } void init() { for (int i = 0; i <= n; ++i) g[i].clear(); for (int i = 0; i < k; ++i) lev[i].clear(); clr(bit, 0); clr(l, 0); clr(r, 0); clr(d, 0); cnt = 0; } int lb(int j, int x) { return lower_bound(lev[j].begin(), lev[j].end(), x) - lev[j].begin() + 1; } int lowbit(int x) { return x & -x; } void add(int j, int pos, int val) { while (pos <= lev[j].size() ) { bit[j][pos] += val; pos += lowbit(pos); } } ll sum(int j, int pos) { ll ans = 0; while (pos) { ans += bit[j][pos]; pos -= lowbit(pos); } return ans; } int main() { int t; int cas = 0; scanf("%d", &t); while (t--) { printf("Case#%d: ", ++cas); scanf("%d%d%d", &n, &m, &k); init(); int u, v; for (int i = 1; i < n; ++i) { scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs(1, 0, 0); while (m--) { int ch, x, v; scanf("%d%d", &ch, &x); if (ch == 1) { scanf("%d", &v); for (int i = 0; i < k; ++i) { int j = (i + d[x]) % k; int ls = lb(j, l[x]), rs = lb(j, r[x]+1); add(j, ls, (i + 1) * v); add(j, rs, (-1 - i) * v); } } else { int j = d[x] % k; int s = lb(j, l[x]); printf("%lld ", sum(j, s)); } } } return 0; }