1 条题解

  • 2
    @ 2024-6-12 19:19:06

    主要考察树上最短路(倍增法求最近公共祖先)与转化法。

    题目大意​:给定一棵 n 个节点的树,每条边边权均为 1,其中有 k 个点在 0 的时间内相互可达。q 次询问,求最少经过多长时间可以从 u 走到 v

    子任务一​:n,k500 且只有一次询问,可以暴力建图(k 个传送点之间连 k(k1)2\frac{k(k-1)}{2} 条边权为 0 的边),并使用双端队列 BFS 解决。

    子任务二​:不存在传送点,问题变为常规地求树上两点最短路。注意到 n≤2×10510^5,使用倍增 LCA 求解即可。

    子任务三​:我们注意到,从节点 u 到节点 v 可以有两种路径可走:可以不走传送点使用 LCA,也可以选择从传送点走。解决本题目的关键点在于如何快速求得从传送点走到路径长度。

    传送过程中消耗的时间为 0,因此只需要计算从 u 到传送点和从传送点到 v 两端路程的长度即可。贪心分析可得,使用的两个传送点一定是分别离 u,v 最近的两个传送点,才能保证消耗的总时间最少。到这里思路已经很明确了:计算离每个点最近的传送点到该节点的距离 dst,最终答案即为 dstu_u+dstv_v

    参考代码:

    #include <iostream>
    #include <vector>
    #include <queue>
    using namespace std;
    
    const int N = 2e5 + 10;
    const int lgN = 30;
    int n, k, s, dep[N], fa[N][lgN], lg[N], dst[N];
    vector<int> G[N];
    queue<int> Q;
    void dfs(int u, int fath) {
    	dep[u] = dep[fath] + 1;
    	fa[u][0] = fath;
    	for (int i = 1; (1 << i) <= dep[u]; i ++) {
    		fa[u][i] = fa[fa[u][i - 1]][i - 1];
    	}
    	for (int i = 0; i < G[u].size(); i ++) {
    		int v = G[u][i];
    		if (v != fath) {
    			dfs(v, u);
    		}
    	}
    }
    int lca(int x, int y) {
    	if (dep[x] < dep[y]) swap(x, y);
    	while (dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]]];
    	if (x == y) return x;
    	for (int k = lg[dep[x]]; k >= 0; k --) {
    		if (fa[x][k] != fa[y][k]) {
    			x = fa[x][k];
    			y = fa[y][k];
    		}
    	}
    	return fa[x][0];
    }
    void bfs() {
    	while (!Q.empty()) {
    		int u = Q.front(); Q.pop();
    		for (int i = 0; i < G[u].size(); i ++) {
    			int v = G[u][i];
    			if (dst[v] == 1e9) {
    				dst[v] = dst[u] + 1;
    				Q.push(v);
    			}
    		}
    	}
    }
    
    int main() {
    	cin >> n >> k >> s;
    	for (int i = 0; i <= n; i ++) dst[i] = 1e9;
    	for (int i = 1; i < n; i ++) {
    		int u, v; cin >> u >> v;
    		G[u].push_back(v); G[v].push_back(u);
    	}
    	lg[1] = 0;
    	for (int i = 2; i <= n; i ++) {
    		lg[i] = lg[i / 2] + 1;
    	}
    	dfs(1, 0);
    	for (int i = 1; i <= k; i ++) {
    		int x; cin >> x;
    		Q.push(x); dst[x] = 0;
    	}
    	bfs();
    	while (s--) {
    		int u, v; cin >> u >> v;
    		cout << min(dst[u] + dst[v]
    		, dep[u] + dep[v] - 2 * dep[lca(u, v)]) << "\n";
    	}
    	return 0;
    }
    
    • 1

    [GESP样题 八级] 小杨的旅游

    信息

    ID
    646
    时间
    1000ms
    内存
    512MiB
    难度
    7
    标签
    递交数
    14
    已通过
    12
    上传者