题意:
给出一颗有点权和边权的树。求每一个点u的子树中有多少点v,使得点v到点u的距离小于等于点v的权值。
题解:
对于每一个点,倍增的预处理出他的祖宗节点及距离。根据预处理的结果求出每个点能到的最远的祖宗节点。
设点u能到的最远祖宗节点为v。利用差分的思想在点tree[u]+1,点tree[v]-1。
最后每个点的答案就是子树的tree[]和。
#includeusing namespace std;typedef long long ll;const int maxn = 5e5+10;int t, n, u, v, tree[maxn], ans[maxn];ll c, x[maxn];struct node { ll val; int nod;};node fa[32][maxn], tmp;vector g[maxn];void dfs(int u, int pre) { fa[0][u].nod = pre; int len = g[u].size(); for(int i = 0; i < len; i++) { int v = g[u][i].nod; if(v==pre) { fa[0][u].val = g[u][i].val; continue; } dfs(v, u); }}void dfs_double(int u, int pre) { int len = g[u].size(); for(int i = 0; i < len; i++) { int v = g[u][i].nod; if(v==pre) continue; dfs_double(v, u); } tree[u]++; ll tv = x[u]; for(int k = 31; k >= 0; k--) { if(fa[k][u].val <= tv) { tv -= fa[k][u].val; u = fa[k][u].nod; } } tree[u]--;}void solve(int u, int pre) { int len = g[u].size(); for(int i = 0; i < len; i++) { int v = g[u][i].nod; if(v==pre) continue; solve(v, u); } for(int i = 0; i < len; i++) { int v = g[u][i].nod; if(v==pre) continue; ans[u] += tree[v]+ans[v]; }}int main() { freopen("car.in","r",stdin); scanf("%d", &t); while(t--) { scanf("%d", &n); memset(tree, 0, sizeof(tree)); memset(ans, 0, sizeof(ans)); for(int i = 1; i <= n; i++) g[i].clear(); for(int i = 1; i <= n; i++) scanf("%lld", &x[i]); for(int i = 1; i < n; i++) { scanf("%d%d%lld", &u, &v, &tmp.val); tmp.nod = v; g[u].push_back(tmp); tmp.nod = u; g[v].push_back(tmp); } dfs(1, 1); for(int k = 0; k < 31; k++) { for(int v = 1; v <= n; v++) { fa[k+1][v].nod = fa[k][fa[k][v].nod].nod; fa[k+1][v].val = fa[k][v].val+fa[k][fa[k][v].nod].val; } } dfs_double(1, 0); solve(1, 0); for(int i = 1; i <= n; i++) { printf("%d", ans[i]); if(i==n) puts(""); else printf(" "); } }}