zoukankan      html  css  js  c++  java
  • [题解] luogu P3379 【模板】最近公共祖先(LCA) 树链剖分

    刚重温了下LCA,写个题解记录下
    Problem

    什么是LCA

    LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先


    举个例子,如图,4和5的最近公共祖先为1,5和6的最近公共祖先为3

    什么是树链刨分

    树链剖分,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组、BST、SPLAY、线段树等)来维护每一条链,复杂度为(O(logN))

    我们令(size[v])为以(v)为根的子树的大小

    重边和重链

    重边,即(u)与其儿子中(size[v])最大的那个点的连边。
    重链,即重边所连成的链

    轻边和轻链

    轻边,即(u)的重边以外的所有边
    轻链,即轻边所连成的链


    如图,图中加粗的点都在重链上

    思路

    树刨后,我们可以发现不在重链上的边减少了,
    我们不妨设每个节点(v)所在链的顶端为(top[v]),父亲节点为(fa[v]),深度为(dep[v])
    两个点(a)(b)往上找,深度大的点为(a)(a)往上跳,
    如果(a)在重链上,就跳到所在重链的链顶的父亲处,
    如果(a)在轻链上,就直接跳到自己父亲上
    (都跳到(fa[top[a]]))上

    具体实现

    • dfs1求出:结点(i)的深度(dep[i]), 以(i)为根的子树大小(siz[i]),结点i的父亲(fa[i]), 并预处理出重链
    • dfs2求出:结点(i)所在重链的链顶(top[i]),如果它不为重节点,直接令(top[i] = i;)
    int LCA(int a, int b) {
    	while (top[a] != top[b]) {
    	//链顶相同时,说明这两点在同一条链上了
    		if(dep[top[a]] < dep[top[b]]) 
    			a = fa[top[a]];
    		else b = fa[top[b]];
    	}//深度大的向上跳
    	if (dep[a] >= dep[b]) return b;
    	else return a;
    }
    

    时间复杂度应为:(O(2n + mlogn))

    CODE

    #include <bits/stdc++.h>
    #define SIZE 500000
    using namespace std;
    
    int n, m, root, x, y, len;
    int f[SIZE + 30], Dep[SIZE + 30], siz[SIZE + 30];
    int Son[SIZE + 30], Top[SIZE + 30], Fa[SIZE + 30];
    
    struct Edge {
    	int s, next;
    }e[SIZE * 4 + 30];
    inline void AddEdge(int u, int v){
        e[++len] = (Edge){v, f[u]};
        f[u] = len;
    }
    
    void dfs1(int s) {//预处理Dep, siz, Fa, Son
    	siz[s] = 1;//加上根
    	for (int i = f[s]; i; i = e[i].next) {
    		int d = e[i].s;
    		if (Fa[s] != d) {//保证此边连的不是父亲
    			Fa[d] = s;//预处理d的父亲为s
    			Dep[d] = Dep[s] + 1;//预处理深度dep
    			dfs1(d);//先求出以d为根的子树的大小同时预处理下面的dep,fa,son
    			siz[s] += siz[d];//以s为根的子树大小相应加上以d为根的子树大小
    			if (siz[Son[s]] < siz[d])
    				Son[s] = d;//查找重节点
    		}
    	}
    }
    void dfs2(int s) {//预处理Top 
    	if (s == Son[Fa[s]])
    		Top[s] = Top[Fa[s]];//重节点的top就是重链的链顶
    	else Top[s] = s;//轻链的链顶为自己
    	for (int i = f[s]; i; i = e[i].next)
    		if (e[i].s != Fa[s])//保证这条边连的不是父亲
    			dfs2(e[i].s);//继续拓展下面的节点的top
    }
    
    int LCA(int a, int b) {
    	while (Top[a] != Top[b]) {
            //链顶相同时,说明这两点在同一条链上了
    		if(Dep[Top[a]] < Dep[Top[b]]) a = Fa[Top[a]];
    		else b = Fa[Top[b]];//深度大的向上跳
    	}//搜到在同一条链上时,返回深度大的值
    	if (Dep[a] >= Dep[b]) return b;
    	else return a;
    }
    
    int main() {
    	scanf ("%d%d%d", &n, &m, &root);
    	for (int i = 1; i < n; ++i) {
    		scanf ("%d%d", &x, &y);
    		AddEdge(x, y); AddEdge(y, x);
    	}//前向星加边
    	Dep[root] = 1;
    	dfs1(root);
    	dfs2(root);
    	for (int i = 1; i <= m; ++i) {
    		scanf ("%d%d", &x, &y);
    		printf("%d
    ", LCA(x, y));
    	}//在线处理,离线可以用tarjan但我不会
    	return 0;
    }
    
  • 相关阅读:
    Codeforces 741D 【Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths】
    Codeforces 235C 【Cyclical Quest】
    UOJ #62.【UR #5】怎样跑得更快
    luoguP3648 [APIO2014]序列分割
    luoguP4782 [模板]2-SAT问题
    原博客已废弃
    个数可变的参数收藏
    新的一年开始。
    文件上传下载总结
    模板模式学习(转)
  • 原文地址:https://www.cnblogs.com/martixx/p/11232422.html
Copyright © 2011-2022 走看看