• 「SPOJ DYNALCA」Dynamic LCA


    Description

    有一个森林最初由 (n) 个互不相连的点构成

    你需要处理以下 (m) 次操作:

    link A B:添加从顶点A到B的边,使 (A) 成为 (B) 的子节点,其中保证 (A) 是一个根顶点,(A)(B) 在不同的树中。

    cut A:切断点 (A) 到其父节点的边,保证 (A) 是一个非根节点。

    lca A B:输出 (A)(B) 的最近共同祖先,保证 (A)(B) 在同一棵树中。

    Hint

    (1le n, mle 10^5)

    Solution

    看到加边和删边,不难想到用 ( exttt{Link-Cut Tree})

    但难点在于求 LCA。

    假设我们有这样一颗树,以 1 为根,并要求 lca 6 9

    JxK1UA.png

    先打通 6 到 根 的实链(红色边为实边),access(6)

    JxM9RP.png

    如果我们再 access(9),那么:

    JxM8Z4.png

    可以发现,原来有一条实边变虚(蓝色边)了,而这条虚边的父结点正是所求的最近公共祖先。

    结论,access(x) 后,再 access(y) 的过程中实变虚的最后一条边就是 LCA(x, y)

    具体做法,可以在 access 函数中加入返回值会好做一些。

    inline int access(int x) {
    	register int y = 0;
    	for (; x; x = fa[y = x]) splay(x), rc = y;
    	return y;
    }
    inline int LCA(int x, int y) {
    	return access(x), access(y);
    }
    

    注意,cut 操作不能 makrRoot,因为 LCA 这个东西是会受根结点的位置影响的,不可随意换根。而 link 由于 (x) 本来就是根,makeRoot 没有关系。

    Code

    /*
     * Author : _Wallace_
     * Source : https://www.cnblogs.com/-Wallace-/
     * Problem : SPOJ DYNALCA Dynamic LCA
     */
    #include <iostream>
    #include <string>
    
    using namespace std;
    const int N = 1e5 + 5;
    
    int ch[N][2], fa[N];
    bool rev[N];
    
    #define lc ch[x][0]
    #define rc ch[x][1]
    
    inline bool isRoot(int x) {
    	return x != ch[fa[x]][0] && x != ch[fa[x]][1];
    }
    inline int getc(int x) {
    	return x == ch[fa[x]][1];
    }
    
    inline void setRev(int x) {
    	swap(lc, rc), rev[x] ^= 1;
    }
    inline void pushdown(int x) {
    	if (rev[x]) {
    		if (lc) setRev(lc);
    		if (rc) setRev(rc);
    		rev[x] = 0;
    	}
    }
    inline void pushdownAll(int x) {
    	if (!isRoot(x)) pushdownAll(fa[x]);
    	pushdown(x);
    }
    
    inline void rotate(int x) {
    	int y = fa[x], z = fa[y];
    	int k = getc(x), w = ch[x][!k];
    	if (!isRoot(y)) ch[z][getc(y)] = x;
    	ch[x][!k] = y, ch[y][k] = w;
    	if (w) fa[w] = y;
    	fa[y] = x, fa[x] = z;
    }
    inline void splay(int x) {
    	pushdownAll(x);
    	for (register int y = fa[x]; !isRoot(x); rotate(x), y = fa[x])
    		if (!isRoot(y)) rotate(getc(x) != getc(y) ? x : y);
    }
    
    inline int access(int x) {
    	register int y = 0;
    	for (; x; x = fa[y = x]) splay(x), rc = y;
    	return y;
    }
    inline void makeRoot(int x) {
    	access(x), splay(x), setRev(x);
    }
    inline void link(int c, int f) {
    	makeRoot(c), fa[c] = f;
    }
    inline void cut(int x) {
    	access(x), splay(x), fa[lc] = 0, lc = 0;
    }
    inline int LCA(int x, int y) {
    	return access(x), access(y);
    }
    
    signed main() {
    	int n, q;
    	ios::sync_with_stdio(false);
    	cin >> n >> q;
    	
    	for (; q; --q) {
    		string cmd; int x, y;
    		cin >> cmd;
    		
    		if (cmd == "lca")
    			cin >> x >> y, cout << LCA(x, y) << endl;
    		if (cmd == "link")
    			cin >> x >> y, link(x, y);
    		if (cmd == "cut")
    			cin >> x, cut(x);
    	}
    	return 0;
    }
    
  • 相关阅读:
    C# 本地文件夹上传至网络服务器中(待续)
    杭州"人才新政22条" 硕士来杭工作一次性补贴2万元
    12.Redis Select 命令
    HTML input 标签不可编辑的 readonly 属性
    让时间处理简单化 【第三方扩展类库org.apache.commons.lang.time】
    java 给指定时间加上天数or给当前日期加天数
    java事务处理
    基于注解的SpringMVC整合JPA
    Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: Cannot open connection
    <%@ include file=""%>与<jsp:include page=""/>两种方式的作用
  • 原文地址:https://www.cnblogs.com/-Wallace-/p/12819613.html
Copyright © 2020-2023  润新知