点分治
点分治是个好东西。
给定一棵有
个点的树,询问树上距离为 的点对是否存在。
首先把询问离线。在之后的过程里一起统计答案。
树上距离
不妨随便在树中选择一个点,然后分类讨论这条路径是否经过这个点。或者说,随便选一个点
如果这条路径不经过
所以我们只需要求解第二种情况,即这条路径经过
此时这条路径的两个端点会有两种情况:
- 一个端点为
; - 两个端点都不为
。
(还有一种两个端点都为
不难发现,后一种路径是由两条前一种路径构成的。例如
但是,
如何做到这个强制?很简单。我们按照顺序枚举
void work(int u, int F) {
// 查询
{}
for (int v : g[u])
if (!vis[v] && v != F) {
work(v, u);
}
// 修改
{}
}
void dfs(int u) {
vis[u] = true;
for (int v : g[u])
if (!vis[v]) {
work(v);
}
// 清空
{}
for (int v : g[u])
if (!vis[v]) {
dfs(v);
}
}
于是分别实现 // 查询 {}
、// 修改 {}
、// 清空 {}
就好了。
对于本题,我们只需要维护一个 bool
数组
那么查询和修改就只需要这样写:
res |= f[k - dis[u]];
f[dis[u]] = true;
清空时直接 memset
复杂度不对。我们需要再进行类似上面的 work
函数。
void work(int u, int F) {
f[dis[u]] = false;
for (int v : g[u])
if (!vis[v] && v != F) {
work(v, u);
}
}
至此,这个算法看起来很对。但是我们每访问到一个点,就去遍历它的所有子树,这样做复杂度实际上是
如何优化?我们思考到底哪个点作为根
树的中心有性质:它的每个儿子的子树的大小
如果选择树的中心作为根节点,那么最多递归
于是复杂度降至
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!