【YBT2023寒假Day2 B】树上距离(分块)(LCA)(DP)

树上距离

题目链接:YBT2023寒假Day2 B

题目大意

一棵树,边有边权,每次给出 l,r,x,求 x 号点走到编号在 l~r 之间最近的点的距离。

思路

这题还有其它方法,比如线段树分治+线段树,点分树+线段树。
这里用的是分块。

考虑按编号分块。
那么散块我们可以暴力枚举点与 x 查询,通过 O(1) 的 LCA 可以做到一次询问是 O(1)

考虑大块的。
考虑把这些块里面的点记作特殊点,然后先从下往上 DP,再从上往下 DP,就可以求出所有点到这个大块的距离。
那那一个数组存着就可以。

总复杂度是 O(nn)
不过这里有点卡常,卡卡就行(指卡了一个中午)。

代码

#include<cmath> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<algorithm> using namespace std; const int N = 1e5 + 100; struct node { int x, to, nxt; }e[N << 1]; int n, m, B, blo[N], bl[N], br[N], fa[N]; int le[N], KK, f[N][220], dfn[N], upv[N]; bool in[N]; void add(int x, int y, int z) { e[++KK] = (node){z, y, le[x]}; le[x] = KK; } struct GET_LCA { int dy[N * 2], deg[N], f[N * 2][19], tot, log2_[N * 2], dis[N]; void dfs(int now, int father) { dfn[++dfn[0]] = now; fa[now] = father; deg[now] = deg[father] + 1; dy[now] = ++tot; f[tot][0] = now; for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { upv[e[i].to] = e[i].x; dis[e[i].to] = dis[now] + e[i].x; dfs(e[i].to, now); f[++tot][0] = now; } } inline void Init() { log2_[0] = -1; for (int i = 1; i <= tot; i++) log2_[i] = log2_[i >> 1] + 1; for (int i = 1; i <= 18; i++) for (int j = 1; j + (1 << i) - 1 <= tot; j++) { int x = f[j][i - 1], y = f[j + (1 << (i - 1))][i - 1]; f[j][i] = (deg[x] < deg[y]) ? x : y; } } inline int LCA(int x, int y) { x = dy[x]; y = dy[y]; if (x > y) swap(x, y); int k = log2_[y - x + 1]; x = f[x][k]; y = f[y - (1 << k) + 1][k]; return (deg[x] < deg[y]) ? x : y; } inline int get_dis(int x, int y) { return dis[x] + dis[y] - 2 * dis[LCA(x, y)]; } }L; inline void DP(int tmp) { for (int i = n; i >= 1; i--) { int now = dfn[i]; f[fa[now]][tmp] = min(f[fa[now]][tmp], f[now][tmp] + upv[now]); } for (int i = 1; i <= n; i++) { int now = dfn[i]; if (fa[now]) f[now][tmp] = min(f[now][tmp], f[fa[now]][tmp] + upv[now]); } } int main() { freopen("tree.in", "r", stdin); freopen("tree.out", "w", stdout); // n = 100000; scanf("%d", &n); for (int i = 1; i < n; i++) { // int x = i, y = i + 1, z = 1; int x, y, z; scanf("%d %d %d", &x, &y, &z); add(x, y, z); add(y, x, z); } // m = 0; scanf("%d", &m); // B = 10; B = 750; L.dfs(1, 0); L.Init(); for (int i = 1; i <= n; i++) { blo[i] = (i - 1) / B + 1; if (!bl[blo[i]]) bl[blo[i]] = i; br[blo[i]] = i; } for (int i = 1; i <= blo[n]; i++) { for (int j = 1; j <= n; j++) f[j][i] = 2e9; for (int j = bl[i]; j <= br[i]; j++) f[j][i] = 0; DP(i); } for (int i = 1; i <= m; i++) { int ans = 2e9; int l, r, x; scanf("%d %d %d", &l, &r, &x); if (blo[l] == blo[r]) { for (int j = l; j <= r; j++) ans = min(ans, L.get_dis(j, x)); } else { for (int j = l; j <= br[blo[l]]; j++) ans = min(ans, L.get_dis(j, x)); for (int j = bl[blo[r]]; j <= r; j++) ans = min(ans, L.get_dis(j, x)); for (int j = blo[l] + 1; j <= blo[r] - 1; j++) ans = min(ans, f[x][j]); } printf("%d\n", ans); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT2023Day2_B.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示