• 修马路


    问题描述

    ⼩迟⽣活的城市是⼀棵树(树指的是⼀个含有 (n) 个节点以及 (n-1) 条边的⽆向连通图),节点编号从 (1)(n),每条边拥有⼀个权值 (value),表⽰通过这条边的时候你需要交纳的⾦钱(注意,有可能这个值为负数,也就是说你经过这条边的时候你可以赚钱)

    ⼩迟是⼀个杰出的马路⼯,他居住在节点 (s),他能够选择任意⼀个节点(m),并将从节点 (s) 到节点 (m) 的简单路径(简单路径指的是不经过同⼀个节点两次)上的所有边的权值都修改为 (0).

    现在⼩迟获得 (q) 个请求,每个请求都是以 (a, b) 的形式给出,表⽰⼩迟的好朋友⼩早希望从节点 (a) ⾛简单路径到节点 (b),⼩迟希望能最⼩化⼩早需要缴纳的钱。

    需要注意的是,⼩迟获得的这 (q) 个请求是相互独⽴的,也就是说您只需要对于每⼀个请求,决定⼩迟的⼀个修路⽅案,使得⼩早需要缴纳的钱尽可能的少。

    输入格式

    输⼊⽂件名为 road.in。

    第⼀⾏三个正整数为 (n,q,s)

    接下来 (n-1) ⾏,每⾏三个整数 (x y z), 表⽰有⼀条边 ((x,y))(value)(z)

    接下来 (q) ⾏,每⾏两个正整数 (a, b),表⽰请求。

    输出格式

    输出⽂件名为 road.out。

    Q ⾏,每⾏⼀个整数,表⽰需要缴纳的最少的钱。

    样例输入

    3 2 1
    1 2 1
    2 3 1
    1 2
    1 3
    

    样例输出

    0
    0
    

    样例解释

    对于第⼀次询问 (1,2), ⼩迟可以修从 (1)(2) 的路,从⽽使得⼩早不需要缴纳⾦钱;

    对于第⼆次询问 (1,3), ⼩迟可以修从 (1)(3) 的路,从⽽使得⼩早不需要缴纳⾦钱。

    数据规模及约定

    对于 30% 的数据,(n≤1000,q≤1000)

    对于 100% 的数据,(1≤n,q≤200000,1≤x,y≤n,|z|≤1000)

    题目分析

    题目给的是一颗无根树,如果我们以小迟的家作为根的话,这个问题相当于你可以删去根到任意节点的权值, 使得 (a)(b) 花费最少

    看到树上的简单路径,就知道这道题一定考察 LCA

    算法一: 在树上倍增求LCA的基础上, 同时维护倍增的最大和, 复杂度 (nlogn), 然而我考场上想出来没Debug出来

    算法二:考虑链上问题, 即求链上最大值。在树链剖分上线段树维护最大值就好了, 复杂度 (nlog^2n)(远远无法达到理论上界)

    #include <cstdio>
    
    int read() {
    	int x = 0, ch = getchar(), f = 0;
    	for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') f = 1;
    	for (; ch >= '0' && ch <= '9'; ch = getchar())
    		x = (x << 1) + (x << 3) + (ch ^ 48);
    	return f ? -x : x;
    }
    
    int max(const int &x, const int &y) {
    	return x > y ? x : y;
    }
    
    #define rep(i, s, t) for (int i = (int)(s); i <= (int)(t); ++ i)
    
    const int maxn = 2e5 + 10;
    
    int ver[maxn << 1], Next[maxn << 1], head[maxn], edge[maxn << 1], tot;
    int sz[maxn], dep[maxn], son[maxn], fa[maxn], pos[maxn], tid[maxn], a[maxn], dfn, top[maxn];
    
    int n, m, root;
    
    inline void addline(int x, int y, int z) {
    	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
    }
    
    inline void swap(int &x, int &y) {
    	int t = y; y = x; x = t;
    }
    
    struct SegmentTree {
    	int l, r, val;
    	SegmentTree() {
    		val = -1e9; l = r = 0;
    	}
    }tree[maxn << 2];
    
    inline void build(int k, int l, int r) {
    	tree[k].l = l, tree[k].r = r;
    	if (l == r) {
    		tree[k].val = a[pos[l]]; return;
    	}
    	int mid = (l + r) >> 1;
    	build(k << 1, l, mid); build(k << 1 | 1, mid + 1, r);
    	tree[k].val = max(tree[k << 1].val, tree[k << 1 | 1].val);
    }
    
    int query(int k, int l, int r) {
    	if (tree[k].l == l && tree[k].r == r) {
    		return tree[k].val;
    	}
    	int mid = (tree[k].l + tree[k].r) >> 1;
    	if (mid >= r) {
    		return query(k << 1, l, r);
    	} else if (mid < l) {
    		return query(k << 1 | 1, l, r);
    	} else {
    		return max(query(k << 1, l, mid), query(k << 1 | 1, mid + 1, r));
    	}
    }
    
    inline void dfs1(int x, int f, int d, int val) {
    	sz[x] = 1, fa[x] = f, dep[x] = d, a[x] = val;
    	for (int i = head[x], y; i; i = Next[i]) {
    		if ((y = ver[i]) == f) continue;
    		dfs1(y, x, d + 1, val + edge[i]); sz[x] += sz[y];
    		if (sz[y] > sz[son[x]]) son[x] = y;
    	}
    }
    
    inline void dfs2(int x, int rt) {
    	top[x] = rt, pos[tid[x] = ++dfn] = x;
    	if (son[x]) dfs2(son[x], rt);
    	for (int i = head[x], y; i; i = Next[i]) {
    		if ((y = ver[i]) != fa[x] && y != son[x]) dfs2(y, y);
    	}
    }
    
    int lca(int x, int y) {
    	if (top[x] == top[y]) return dep[x] < dep[y] ? x : 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;
    }
    
    inline void Pre() {
    	dfs1(root, root, 0, 0), dfs2(root, root), build(1, 1, n);
    }
    
    int jump(int f, int t) {
    	if (top[f] == top[t]) {
    		return query(1, tid[t], tid[f]);
    	}
    	int ans = -1e9;
    	while (top[f] != top[t]) {
    		ans = max(ans, query(1, tid[top[f]], tid[f]));
    		f = fa[top[f]];
    	}
    	ans = max(ans, query(1, tid[t], tid[f]));
    	return ans;
    }
    
    int query(int x, int y) {
    	int Lca = lca(x, y), aa = jump(x, Lca) - a[Lca], ba = jump(y, Lca) - a[Lca];
    	int ans = a[x] + a[y] - 2 * a[Lca];
    	aa = max(aa, 0); ba = max(ba, 0);
    	return ans - max(aa, ba);
    }
    
    int main() {
    	n = read(), m = read(), root = read();
    	int x, y, z;
    	rep(i, 1, n - 1)
    		x = read(), y = read(), z = read(),
    		addline(x, y, z), addline(y, x, z);
    	Pre();
    	rep(i, 1, m) {
    		x = read(), y = read();
    		printf("%d
    ", query(x, y));
    	}
    }
    
  • 相关阅读:
    「考试」省选27
    「考试」省选26
    「考试」省选25
    $dy$讲课总结
    「笔记」$Min\_25$筛
    「考试」省选24
    「总结」多项式生成函数例题(4)
    「总结」多项式生成函数相关(4)
    「考试」省选23
    「总结」后缀3
  • 原文地址:https://www.cnblogs.com/Alessandro/p/9929744.html
Copyright © 2020-2023  润新知