好像是某CF的题,不记得……
很套路的题,但是觉得可以做一下笔记。
倍增 + 差分。
有一个比较简单的思路就是每一个点$x$向上走一走,直到走到一个点$y$使总路程恰好不超过超过了$L$,然后把$(x, y)$这条链上的答案$ + 1$。
可以用倍增优化走一走的过程,可以用差分实现把一条向上的树链$+ 1$的操作。
时间复杂度$O(nlogn)$。
Code:
#include <cstdio> #include <cstring> using namespace std; typedef long long ll; const int N = 2e5 + 5; const int Lg = 20; int n, tot = 0, head[N], fa[N][Lg], f[N]; ll cur, dis[N][Lg]; struct Edge { int to, nxt; ll val; } e[N << 1]; inline void add(int from, int to, ll val) { e[++tot].to = to; e[tot].val = val; e[tot].nxt = head[from]; head[from] = tot; } template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } void dfs(int x, int fat, ll nowDis) { fa[x][0] = fat, dis[x][0] = nowDis; for(int i = 1; i <= 18; i++) { fa[x][i] = fa[fa[x][i - 1]][i - 1]; dis[x][i] = dis[x][i - 1] + dis[fa[x][i - 1]][i - 1]; } for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fat) continue; dfs(y, x, e[i].val); } } inline int getPos(int x) { ll nowDis = 0LL; for(int i = 18; i >= 0; i--) if(nowDis + dis[x][i] <= cur) { nowDis += dis[x][i]; x = fa[x][i]; } return x; } void solve(int x, int fat) { for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fat) continue; solve(y, x); f[x] += f[y]; } } int main() { read(n), read(cur); for(int i = 2; i <= n; i++) { int fat; ll v; read(fat), read(v); add(fat, i, v), add(i, fat, v); } dfs(1, 0, 0LL); /* for(int i = 1; i <= n; i++) printf("%d ", dis[i][18]); */ for(int i = 1; i <= n; i++) { ++f[i]; int pos = getPos(i); --f[fa[pos][0]]; } solve(1, 0); for(int i = 1; i <= n; i++) printf("%d\n", f[i]); return 0; }