• [bzoj1787][Ahoi2008]Meet 紧急集合


    题目大意:给你一棵树,和$3$个节点,要你找到树上的一个点,使得三个点到这个点的距离和最小,并输出个距离

    题解:令三个点为$a,b,c$,$i,j$两点的$lca$为$lca_{i,j}$,第$i$个点的深度为$depth_i$,$i,j$两点之间的距离为$d_{i,j}$。所以会发现$lca_{a,b},lca_{b,c},lca_{a,c}$中至少有两个是相同的。

    假设$lca_{a,b}==lca_{a,c}$:

    $ herefore lca_{a,b,c}==lca_{a,b}==lca_{a,c}$
    $$
    egin{align*}
    ans&=d_{b,c}+d_{lca_{b,c},a}\
    &=d_{b,lca_{b,c}}+d_{lca_{b,c},c}+d_{lca_{b,c},lca_{a,b,c}}+d_{lca_{a,b,c},a}\
    &=depth_b+depth_c-2 cdot depth_{lca_{b,c}}+depth_{lca_{b,c}}+depth_a-2cdot depth_{lca_{a,b}}\
    &=depth_a+depth_b+depth_c+depth_{lca_{b,c}}-2cdot depth_{lca_{a,b}}
    end{align*}
    $$
    再常规化:
    $$
    ans=depth_a+depth_b+depth_c+depth+max{depth_{lca_{a,b}},depth_{lca_{a,c}},depth_{lca_{b,c}}}-2cdotmin{depth_{lca_{a,b}},depth_{lca_{a,c}},depth_{lca_{b,c}}}
    $$
    卡点:倍增求$LCA$时$for$循环未循环到$1$



    C++ Code:

    #include <cstdio>
    #define maxn 500010
    #define lb(x) (x & -x)
    using namespace std;
    int n, m;
    inline void swap(int &a, int &b) {a ^= b ^= a ^= b;}
    
    int head[maxn], cnt;
    struct Edge {
    	int to, nxt;
    } e[maxn << 1];
    void addE(int a, int b) {
    	e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
    }
    
    int dad[maxn][19], dep[maxn];
    void dfs(int rt) {
    	for (int i = head[rt]; i; i = e[i].nxt) {
    		int v = e[i].to;
    		if (v != dad[rt][0]) {
    			dep[v] = dep[rt] + 1;
    			dad[v][0] = rt;
    			dfs(v);
    		}
    	}
    }
    void init() {
    	for (int i = 1; i < 19; i++) {
    		for (int j = 1; j <= n; j++) {
    			dad[j][i] = dad[dad[j][i - 1]][i - 1];
    		}
    	}
    }
    int LCA(int x, int y) {
    	if (x == y) return x;
    	if (dep[x] < dep[y]) swap(x, y);
    	for (int t = dep[x] - dep[y]; t; t &= t - 1) x = dad[x][__builtin_ctz(lb(t))];
    	if (x == y) return x;
    	for (int i = 18; ~i; i--) if (dad[x][i] != dad[y][i]) x = dad[x][i], y = dad[y][i];
    	return dad[x][0];
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i < n; i++) {
    		int a, b;
    		scanf("%d%d", &a, &b);
    		addE(a, b);
    		addE(b, a);
    	}
    	dep[1] = 1;
    	dfs(1);
    	init();
    	while (m --> 0) {
    		int a, b, c, ans = 0;
    		scanf("%d%d%d", &a, &b, &c);
    		int lca_ab = LCA(a, b), lca_ac = LCA(a, c), lca_bc = LCA(b, c);
    		printf("%d ", lca_ab ^ lca_ac ^ lca_bc);
    		if (lca_ab == lca_ac) ans = dep[b] + dep[c] - dep[lca_bc] + dep[a] - (dep[lca_ac] << 1);
    		else {
    			if (lca_bc == lca_ab) ans = dep[a] + dep[c] - dep[lca_ac] + dep[b] - (dep[lca_ab] << 1);
    			else ans = dep[a] + dep[b] - dep[lca_ab] + dep[c] - (dep[lca_ac] << 1);
    		}
    		printf("%d
    ", ans);
    		
    	}
    	return 0;
    }
    





  • 相关阅读:
    url 路径的拼接
    java 实现导出Excel文件
    window 使用频率最高的快捷键
    jeesite 框架的简单应用
    一个软件开发者的解决问题的心得——善于利用蛛丝马迹
    在linux上安装dotnetcore
    c#多线程同步之EventWaitHandle使用
    sharepoint 2013 安装
    模板模式的应用
    正则表达式的应用
  • 原文地址:https://www.cnblogs.com/Memory-of-winter/p/9513179.html
Copyright © 2020-2023  润新知