zoukankan      html  css  js  c++  java
  • P4281 [AHOI2008]紧急集合 / 聚会

    P4281 [AHOI2008]紧急集合 / 聚会

    给定一棵树, 边权为 (1), 多次询问, 给出三个点 (a, b, c) 求三个点的中点和到中点的总距离
    定义中点为 (x) 满足 (dis(a, x) + dis(b, x) + dis(c, x)) 最小


    调试日志: (jump) 数组开小, 只开了 (19) ...


    Solution

    首先是树上两点距离:$$dis(x, y) = dep[x] + dep[y] - 2 * dep[LCA(x, y)]$$
    根到 (LCA) 的距离计算了两遍, 减去, 即可得到上式

    回到这题, 发现三点两两 (LCA) 中总有两个一样
    为什么呢? 设 (x = LCA(a, b))(LCA(x, c)) 即为重合的点, 因为此点是 (x) 的祖先, 必然是 (a, b) 的祖先
    然后 找规律 可得集合点一定是三个 (LCA) 中深度最大的那个
    我呸
    可以发现, 深度大的那个 (LCA) 掌管着更深的两个点, 用反证法, 若是将集合点上移 (d) 对于一个点 (-d) 对于另外深度大两个点一共 (+2d) , 即总距离 (+d) , 不优
    而若是将集合点下移 (d) , 那么此点将不在三点的公共路线上, 必然不优
    综上可得: 集合点在三个 (LCA) 中深度大的点

    然后套距离公式, 发现化简得如下式子$$min dis(a, b, c) = dep[a] + dep[b] + dep[c] - dep[lca1] - dep[lca2] - dep[lca3]$$

    Code

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    using namespace std;
    int RD(){
        int out = 0,flag = 1;char c = getchar();
        while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
        while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
        return flag * out;
        }
    const int maxn = 1000019,INF = 1e9 + 19;
    int head[maxn],nume = 1;
    struct Node{
        int v,dis,nxt;
        }E[maxn << 3];
    void add(int u,int v,int dis){
        E[++nume].nxt = head[u];
        E[nume].v = v;
        E[nume].dis = dis;
        head[u] = nume;
        }
    int num, na;
    int dep[maxn], jump[maxn][25];
    void dfs(int id, int F){//处理出深度用于计算距离
    	for(int i = head[id];i;i = E[i].nxt){
    		int v = E[i].v;
    		if(v == F)continue;
    		dep[v] = dep[id] + 1;
    		jump[v][0] = id;
    		dfs(v, id);
    		}
    	}
    void get_jump(){
    	for(int i = 1;i <= 19;i++){
    		for(int j = 1;j <= num;j++){
    			jump[j][i] = jump[jump[j][i - 1]][i - 1];
    			}
    		}
    	}
    int LCA(int x, int y){
    	if(dep[x] < dep[y])swap(x, y);
    	for(int i = 19;i >= 0;i--)if(dep[jump[x][i]] >= dep[y])x = jump[x][i];
    	if(x == y)return x;
    	for(int i = 19;i >= 0;i--){
    		if(jump[x][i] != jump[y][i])x = jump[x][i], y = jump[y][i];
    		}
    	return jump[x][0];
    	}
    int main(){
    	num = RD(), na = RD();
    	for(int i = 1;i <= num - 1;i++){
    		int u = RD(), v = RD();
    		add(u, v, 1), add(v, u, 1);
    		}
    	dep[1] = 1;
    	dfs(1, -1), get_jump();
    	while(na--){
    		int a = RD(), b = RD(), c = RD();
    		int lca1 = LCA(a, b), lca2 = LCA(a, c), lca3 = LCA(b, c);
    		int ans = dep[lca1] > dep[lca2] ? lca1 : lca2;
    		ans = dep[lca3] > dep[ans] ? lca3 : ans;
    		int dis = dep[a] + dep[b] + dep[c] - dep[lca1] - dep[lca2] - dep[lca3];
    		printf("%d %d
    ", ans, dis);
    		}
    	return 0;
    	}
    
  • 相关阅读:
    LeetCode 25. Reverse Nodes in k-Group
    LeetCode 66. Plus One
    LeetCode 69. Sqrt(x)
    很认真的聊一聊程序员的自我修养
    LeetCode 24. Swap Nodes in Pairs
    unordered_map和map的区别
    子查询一定要注意,别忘记加TOP 1,不然就GG了,过了好久测试给我测出来了
    Tree 通过父id找所有子节点
    SqlSugar CURD
    什么是.NET Framwork
  • 原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9720860.html
Copyright © 2011-2022 走看看