代码编织梦想

树上距离

题目链接:YBT2023寒假Day2 B

题目大意

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

思路

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

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

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

总复杂度是 O ( n n ) O(n\sqrt{n}) 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;
}
要转发的话记得附上链接哦~(不过也不会有人转发的啦)
本文链接:https://blog.csdn.net/weixin_43346722/article/details/128795158

【ybt高效进阶4-5-3】树上距离-爱代码爱编程

树上距离 题目链接:ybt高效进阶4-5-3 题目大意 给你一棵有边权的树,多组询问每次求两个点之间的树上距离。 思路 直接上 LCA,没什么好说的。 代码 #include<cstdio> #include<algorithm> using namespace std; struct node { int x,

【ybt高效进阶5-4-1】树上求和(树形DP)-爱代码爱编程

树上求和 题目链接:ybt高效进阶5-4-1 题目大意 给你一棵树,要你选一些点,使得选的点之间没有父子关系,而且点权和最大。 思路 明显的树形DP。 设 f i

【ybt高效进阶5-4-2】结点覆盖(树形DP)-爱代码爱编程

结点覆盖 题目链接:ybt高效进阶5-4-2 题目大意 给你一棵树,选一个点有各自的费用。 然后选一个点可以覆盖它,它的儿子和它的父亲。 问你要覆盖整一个树的最小费用。 思路 一个点能覆盖自己父亲儿子,那一个点一定会被它自己或者父亲或者儿子覆盖到。 设 f

【ybt高效进阶5-4-3】最长距离(树形DP)-爱代码爱编程

最长距离 题目链接:ybt高效进阶5-4-3 题目大意 给你一棵树,边有距离,对于每个点,要你求跟它距离最远的点到它的距离。 思路 看到这种题,因为是最大值所以换根 DP 不太好搞,所以我们就考虑直接树形 DP。 那这样的话距离要么就是直接向下,要么就是向上再向下。 直接向下很好搞,接着问题是第二个。 那你考虑可能是父亲已经搞好,然后要搞儿子,

【YBT2022寒假Day1 A】变量观测(模拟)(分治)-爱代码爱编程

变量观测 题目链接: YBT2022寒假Day1 A 题目大意 给你 n 个数,要你在线维护两种操作: 给一个数加一个值,或者设立一个观察者观察一些数,从当时开始观察,当观察的数的变化值的和大于一个设定值是结束观察。 然后对于每个加数的操作你要输出有多少个观察者结束观察。 思路 发现一个人监视的顶多三个人,我们考虑用这么一个神奇的方法。 (因为如

【YBT2022寒假Day1 B】方格填写(插头DP)-爱代码爱编程

方格填写 题目链接:YBT2022寒假Day1 B 题目大意 有一个二维网格,然后里面每个位置要放一个 0~4 的数有一些已经填好的,也有要你填的。 然后一个位置可以跟相邻的四个点连边,然后要求一个点的边数量等于填的数字。 然后问你所有填写方案边的匹配方案数的平方的和。 思路 考虑这个平方的意义。 它其实是可以变成你选任意一种填数方式,在这中间任

【YBT2022寒假Day2 A】期望旅行(Dij)(期望DP)-爱代码爱编程

期望旅行 题目链接:YBT2022寒假Day2 A 题目大意 给你一个无向图,然后每个边有出现的概率,自环必定出现。 然后问你在最优策略下你从 1 1 1 点走到

【YBT2022寒假Day2 B】【luogu CF809D】模糊序列 / Hitchhiking in the Baltic States(平衡树优化DP)(fhq-Treap)-爱代码爱编程

模糊序列 / Hitchhiking in the Baltic States 题目链接:YBT2022寒假Day2 B / luogu CF809D 题目大意 给你一个序列,然后每个位置有可以选的数的范围。 然后要你找到对于所有可能的序列它严格上升子序列的最大长度。 思路 很明显的 DP。 考虑列方程,发现数的范围很大,考虑从答案序列长度下手,

【YBT2022寒假Day3 A】森林之和(prufer序列)(DP)-爱代码爱编程

森林之和 题目链接:YBT2022寒假Day3 A 题目大意 求对于 n 个有编号点构成的森林,求每种森林每个点度数的平方和的和。 思路 我们看到有有关所有编号树,不难想到 prufer 序列和矩阵树。 那这里应该就是利用 prufer 序列来 DP 答案。 但它是森林,那我们考虑先求出构造一棵树的,然后再依次把树放在一起。 那根据 prufe

【YBT2022寒假Day3 B】【LOJ 2460】欧拉回路 / 桥(二分)(欧拉回路)(网络流)-爱代码爱编程

欧拉回路 / 桥 题目链接:YBT2022寒假Day3 B / LOJ 2460 题目大意 给你一个图,边是双向且两边走的费用不同。 要你选一些边来走,然后使得形成一条欧拉回路,且所选边的最大费用最小。 要输出这个费用和选的边。 思路 首先这个最大费用最小直接用二分。 那么接着就是判断了,我们可以把边分成两类: 只能有一个方向的和两个方向都可以

【YBT2022寒假Day3 C】毒瘤染色(LCT)(圆方树)(容斥)-爱代码爱编程

毒瘤染色 题目链接:YBT2022寒假Day3 C 题目大意 要你在线实现一个操作: 一开始有 n 个点,没有边,然后操作会给你一条边。 如果保证加了之后这个图还是沙漠就加上。 然后每次加完边之后问你一开始所有点都是白色做 k 次每次随机选一个点(可能白色)的点把它变成白色,然后问你分别保留黑白点是的连通块个数的期望值。 (有部分分只用求保留白点的)

【YBT2022寒假Day4 B】连通的图(结论)(树上差分)(线性基)-爱代码爱编程

连通的图 题目链接:YBT2022寒假Day4 B 题目大意 给你一个 n 个点 n+k-1 条边的无向连通图。 然后问你有多少种删边方案(可以不删)使得这个图还是连通。 思路 我们考虑先随便建一棵树,然后分出树边和非树边。 然后这里有个神奇的东西:如果我们让第 i

【YBT2022寒假Day6 B】【luogu CF802C】大图书馆 / Heidi and Library (hard)(网络流)-爱代码爱编程

大图书馆 / Heidi and Library (hard) 题目链接:YBT2022寒假Day6 B / luogu CF802C 题目大意 你有一个容量为 k 的桶,然后每天有需求要一个编号的东西,这天结束时会还回来。 然后你可以花一定的钱买入一个编号的东西,或者把东西扔掉,买的东西不能超过容量数,然后问你最小花费。 思路 考虑网络流建图。

【YBT2022寒假Day8 A】染色计划(Tarjan)(线段树优化建边)(树链剖分)-爱代码爱编程

染色计划 题目链接:YBT2022寒假Day8 A 题目大意 给你一棵树,然后有 k 中颜色,每个点有一个颜色,然后问你要修改多少次,才能使得一个存在的颜色的所有点构成一个连通块。 一个修改操作指选择一个颜色把这个颜色的所有点的颜色改成另一个颜色。 思路 我们考虑如果选一个颜色,我们可以把这个颜色的点弄一棵树,然后路径上时一些别的颜色点构成的链。

【YBT2022寒假Day9 A】最小划分(wqs二分)(斜率优化DP)-爱代码爱编程

最小划分 题目链接:YBT2022寒假Day9 A 题目大意 给你一个序列,你要把它划分成 m 个连续的段,以最小化这个东西: 把每一段的数和表示为 w[i],则要最小化每个 (w[i]+p)^2 的和。 思路 首先你发现这个 p

【YBT2022寒假Day9 B】【luogu CF464E】进制路径 / The Classic Problem(最短路)(主席树)(哈希)-爱代码爱编程

进制路径 / The Classic Problem 题目链接:YBT2022寒假Day9 B / luogu CF464E 题目大意 给你一个无向图,然后边的权值是 2^x 次方,然后要你求一个点到另一个点的最短路径。 思路 首先暴力做法直接跑 Dij。 然后问题在什么地方呢? 边权加在一起和比较。 考虑它是二进制,考虑模拟一下二进制加的过

【YBT2022寒假Day7 A】字符变换(状压)(Tarjan)(DP)-爱代码爱编程

字符变换 题目链接:YBT2022寒假Day7 A 题目大意 给你一个只包含四种字符的字符串,和一些二元组 (x,y),x,y 为两个等长的字符串。 一个二元组的作用是可以选择字符串中一个与 x 相同的子串把它变成 y。 你也可以任意交换字符串两个位置的字符。 然后问你从任意字符串开始进行任意次变换,最多能得到多少个字符串。 思路 你考虑因为可以

【YBT2022寒假Day7 B】格子染色(博弈论)(分类讨论)(找规律打表)-爱代码爱编程

格子染色 题目链接:YBT2022寒假Day7 B 题目大意 有一个长度为 n 的纸条,然后有 k 种颜色,一些位置一开始已经染色颜色,保证相邻颜色不相同。 两人轮流操作,选一个一个未染色的地方染色,保证不能出现相邻位置颜色相同,谁无法操作谁就输了。 然后问你先手是否必胜。 思路 考虑对于

【YBT2022寒假Day7 C】【luogu CF603E】以线覆圆 / Arcs on a Circle(期望)(DP)-爱代码爱编程

以线覆圆 / Arcs on a Circle 题目链接:YBT2022寒假Day7 C / luogu AT3860 题目大意 给你一个周长为 n 的圆和一些长度的线段。 然后每条线段会随机出现在圆中,然后问你这些线段把整个圆覆盖的期望。 思路 我们首先考虑如果线段端点只能是整数,那我们不难有个方法:断环为链来 DP。(然后为了保证不重,我们保

【ybt2023寒假day1 b】不跪模样(树链剖分)(线段树)-爱代码爱编程

不跪模样 题目链接:YBT2023寒假Day1 B 题目大意 给你一棵有根数,点有点权,两种操作: 对于所有 x 子树内与 x 距离不超过 2 的点,将其点权加 v。 询问 x 子树中,满足 i<=j 且 i,j