P3591 [POI2015]ODW
给定一颗有nnn个节点的树,点有点权,给定一个长度为nnn的排列ppp,给定一个长度为n−1n - 1n−1的数组ccc,
我们会在树上进行n−1n - 1n−1次行走,第iii次我们会从p[i]p[i]p[i]走到p[i+1]p[i + 1]p[i+1],步长为c[i]c[i]c[i],且走最短路,
当我们到p[i+1]p[i + 1]p[i+1]的距离小于c[i]c[i]c[i]的时候,我们会直接到点p[i+1]p[i + 1]p[i+1],对每次行走,我们要求出其所经过点的点权和。
由于nnn只有500005000050000,所以写一些比较暴力的算法,先选定根节点为111号节点:
如果c[i]c[i]c[i]大于n\sqrt nn,从p[i]p[i]p[i]到p[i+1]p[i + 1]p[i+1],最多只要跳n\sqrt nn个点,所以,可以直接暴力跳,然后找第kkk代祖先,单次操作O(nlogn)O(\sqrt n \log n)O(nlogn)的复杂度。
如果c[i]c[i]c[i]小于n\sqrt nn,我们定义sum[rt][len]sum[rt][len]sum[rt][len],表示从点rtrtrt向上跳步长为lenlenlen,跳出根节点的路径和,预处理这一步大概是O(nnlogn)O(n \sqrt n \log \sqrt n)O(nnlogn)的复杂度。
之后只要求出lcalcalca,然后二分一下跳几次,在某条链上跳到哪,kkk代祖先找一下点,最后统计一下答案即可,整体复杂度O(nnlogn)O(n \sqrt n \log n)O(nnlogn)。
#include <bits/stdc++.h>using namespace std;const int N = 5e4 + 10;int head[N], to[N << 1], nex[N << 1], cnt = 1;int sz[N], son[N], fa[N], id[N], rk[N], top[N], dep[N], tot;int a[N], p[N], c[N], n, block;long long s[N][250];void add(int x, int y) {to[cnt] = y;nex[cnt] = head[x];head[x] = cnt++;
}void dfs1(int rt, int f) {fa[rt] = f, dep[rt] = dep[f] + 1, sz[rt] = 1;for (int i = head[rt]; i; i = nex[i]) {if (to[i] == f) {continue;}dfs1(to[i], rt);sz[rt] += sz[to[i]];if (!son[rt] || sz[to[i]] > sz[son[rt]]) {son[rt] = to[i];}}
}void dfs2(int rt, int tp) {top[rt] = tp, id[rt] = ++tot, rk[tot] = rt;if (!son[rt]) {return ;}dfs2(son[rt], tp);for (int i = head[rt]; i; i = nex[i]) {if (to[i] == fa[rt] || to[i] == son[rt]) {continue;}dfs2(to[i], to[i]);}
}int lca(int x, int y) {while (top[x] != top[y]) {if (dep[top[x]] < dep[top[y]]) {swap(x, y);}x = fa[top[x]];}return dep[x] < dep[y] ? x : y;
}int k_fa(int rt, int k) {if (dep[rt] <= k) {return 0;}while (k > dep[rt] - dep[top[rt]]) {k -= dep[rt] - dep[top[rt]] + 1;rt = fa[top[rt]];}return rk[id[rt] - k];
}void dfs3(int rt, int fa) {for (int i = 1; i <= block; i++) {s[rt][i] = a[rt] + s[k_fa(rt, i)][i];}for (int i = head[rt]; i; i = nex[i]) {if (to[i] == fa) {continue;}dfs3(to[i], rt);}
}long long f1(int u, int v, int c) {// 暴力跳int f = lca(u, v);if (dep[u] < dep[v]) {swap(u, v);}int cur = u;long long ans = a[u];while (dep[cur] - dep[f] >= c) {cur = k_fa(cur, c);ans += a[cur];}if (v == f) {// 只有一条链if (cur != v) {ans += a[v];}return ans;}if (dep[cur] + dep[v] - 2 * dep[f] <= c) {// 不能跳到链的另一端ans += a[v];return ans;}cur = k_fa(v, dep[v] - dep[f] - (c - (dep[cur] - dep[f])));ans += a[cur];while (dep[v] - dep[cur] >= c) {cur = k_fa(v, dep[v] - dep[cur] - c);ans += a[cur];}if (cur != v) {ans += a[v];}return ans;
}long long f2(int u, int v, int c) {// 优化跳。int f = lca(u, v);if (dep[u] < dep[v]) {swap(u, v);}long long ans = 0;int steps = (dep[u] - dep[f]) / c, cur = k_fa(u, steps * c), now;ans += s[u][c] - s[cur][c] + a[cur];if (v == f) { // 只有一条链if (cur != v) {ans += a[v];}return ans;}if (dep[cur] + dep[v] - 2 * dep[f] <= c) {// 不能跳到链的另一端ans += a[v];return ans;}cur = k_fa(v, dep[v] - dep[f] - (c - (dep[cur] - dep[f])));steps = (dep[v] - dep[cur]) / c, now = k_fa(v, dep[v] - dep[cur] - steps * c);ans += s[now][c] - s[cur][c] + a[cur], cur = now;if (cur != v) {ans += a[v];}return ans;
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);scanf("%d", &n), block = sqrt(n);for (int i = 1; i <= n; i++) {scanf("%d", &a[i]);}for (int i = 1, u, v; i < n; i++) {scanf("%d %d", &u, &v);add(u, v);add(v, u);}for (int i = 1; i <= n; i++) {scanf("%d", &p[i]);}for (int i = 1; i < n; i++) {scanf("%d", &c[i]);}dfs1(1, 0);dfs2(1, 1);dfs3(1, 0);for (int i = 1; i < n; i++) {printf("%lld\n", c[i] > block ? f1(p[i], p[i + 1], c[i]) : f2(p[i], p[i + 1], c[i]));}return 0;
}