[lnsyoj2240/luoguP3591]ODW
题意
给定一棵 个节点的树和数列 ,分别表示点权,移动序列和步长。在第 次移动中,将会从节点 移动到节点 ,步长为 。求移动时经过的所有点的点权之和。
赛时 0PTS
赛后
对于一条路径 ,我们将其拆成 ,这样,我们只需要分别计算 , 这两条路径。
考虑一种暴力的算法:每次 向上跳 步,并累加答案。显然,当 较大时,这种做法可行,但较小时就会超过时限。
另一种可行的算法是:预处理出从第 个节点向上每次跳 个节点,一直到根所经过的所有点的点权之和,即,然后利用类似差分的方法, 求出和。但是这样做 较大时,会超过空间(甚至无法通过编译)
因此,我们设计出了满足两种情况的算法,因此我们考虑根号分治,设计阈值 ,若 ,则使用暴力算法;若 ,则使用预处理算法。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 50005, M = 50005, K = 16, SMAX = 230;
int h[N], e[M], ne[M], idx;
int a[N], b[N], c[N];
int n;
int f[N][K], depth[N];
int g[N][SMAX];
int S;
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void dfs_init(int u, int fa){
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if (j == fa) continue;
depth[j] = depth[u] + 1;
f[j][0] = u;
dfs_init(j, u);
}
}
int up(int u, int k){
for (int i = 0; i < K; i ++ ) {
if (k & 1) u = f[u][i];
k >>= 1;
}
return u;
}
void dfs_init2(int u, int fa){
for (int k = 1; k <= S; k ++ )
g[u][k] = g[up(u, k)][k] + a[u];
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if (j == fa) continue;
dfs_init2(j, u);
}
}
void init(){
depth[1] = 1;
dfs_init(1, -1);
S = sqrt(n);
for (int k = 1; k < K; k ++ )
for (int i = 1; i <= n; i ++ )
f[i][k] = f[f[i][k - 1]][k - 1];
dfs_init2(1, -1);
}
int lca(int u, int v){
if (depth[u] < depth[v]) swap(u, v);
for (int i = K - 1; i >= 0; i -- ) {
int ne = f[u][i];
if (depth[ne] >= depth[v]) u = ne;
}
if (u == v) return u;
for (int i = K - 1; i >= 0; i -- ) {
int nu = f[u][i], nv = f[v][i];
if (nu != nv) u = nu, v = nv;
}
return f[u][0];
}
void solve1(int st, int ed, int step){
int r = lca(st, ed);
int ans = 0;
int u = st;
while (depth[u] >= depth[r]){
ans += a[u];
u = up(u, step);
}
u = ed;
while (depth[u] > depth[r]){
ans += a[u];
u = up(u, step);
}
printf("%d\n", ans);
}
void solve2(int st, int ed, int step){
int r = lca(st, ed);
int ans = 0;
bool flag = true;
int ueddist = step - (depth[st] - depth[r]) % step;
if (ueddist != step) flag = false;
int ued = up(r, ueddist);
ans += g[st][step] - g[ued][step];
ueddist = step - (depth[ed] - depth[r]) % step;
if (ueddist != step) flag = false;
ued = up(r, ueddist);
ans += g[ed][step] - g[ued][step];
if (flag) ans -= a[r];
printf("%d\n", ans);
}
int main(){
memset(h, -1, sizeof h);
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i < n; i ++ ){
int u, v;
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
for (int i = 1; i <= n; i ++ ) scanf("%d", &b[i]);
for (int i = 1; i < n; i ++ ) scanf("%d", &c[i]);
init();
for (int i = 1; i < n; i ++ ){
int st = b[i], ed = b[i + 1], step = c[i];
if (step > S) solve1(st, ed, step);
else solve2(st, ed, step);
}
return 0;
}
分类:
题解 / 2024赛时
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现