• Loj题解 #10132 异象石


    前置知识

    • 倍增/树剖 求LCA
    • set/Splay/平衡树 维护一个时间戳序列

    Description

    给定一棵 \(n\) 个结点的树,边有边权,三种操作:

    • 标记一个点
    • 取消标记一个点
    • 求使标记的所有点联通最少需要连的边的长度

    Solution

    一个很神奇的结论

    记一个数组 \(dfn\) ,用来存dfs序的每个节点
    设现有的被标记的点的dfs序从小到大排序后的集合为 \(\{ x_1, x_2, x_3 ... x_k \}\)
    设两个点 \(u, v\) 的距离为 \(dis_{u, v}\)
    那么答案为 \(dis_{x_1, x_2} + dis_{x_2 + x_3} + ... + dis_{x_{k - 1}, x_k} + dis_{x_k, x_1}\) 的一半

    那么我们用一个数据结构维护标记的点的序列即可,维护的时候顺便统计答案,询问时直接输出
    我选择Splay,因为我不会set

    对答案的统计:
    当标记一个点时,设这个点的dfs序为 \(x\),序列中上一个点为 \(l\),下一个点为 \(r\),那么新增点的贡献为

    \[\large dis_{pre_l, pre_x} + dis_{pre_x, pre_r} - dis_{pre_l, pre_r} \]

    取消标记一个点的处理方式类似(刚好反着)
    注意序列里维护的是结点的dfs序,而求dis是用的是结点的编号
    如果序列中没有 \(l\)\(r\) 时注意特判一下

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define int long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 4e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    struct edge{
        int to, w, nxt;
    }e[MAXN << 1];
    int head[MAXN], num_edge;
    
    int n, m, Ans = 0, Cnt = 0;
    int dis[MAXN], cnt = 0;
    int dep[MAXN], siz[MAXN], son[MAXN], fath[MAXN], top[MAXN], dfn[MAXN], pre[MAXN];
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    void add_edge(int from, int to, int w) { e[++num_edge] = (edge){to, w, head[from]}, head[from] = num_edge; }
    
    namespace Cut{
        void dfs(int u, int fa) {
            dep[u] = dep[fa] + 1, siz[u] = 1, fath[u] = fa;
            for(int i = head[u]; i; i = e[i].nxt) {
                int v = e[i].to;
                if(v == fa) continue;
                dis[v] = dis[u] + e[i].w;
                dfs(v, u);
                siz[u] += siz[v];
                if(siz[son[u]] < siz[v]) son[u] = v;
            }
        }
        void dfs2(int u, int tp) {
            dfn[u] = ++cnt, pre[cnt] = u, top[u] = tp;
            if(son[u]) dfs2(son[u], tp);
            for(int i = head[u]; i; i = e[i].nxt) {
                int v = e[i].to;
                if(v == fath[u] || v == son[u]) continue;
                dfs2(v, v);
            }
        }
        int Get_Lca(int u, int v) {
            while(top[u] != top[v]) dep[top[u]] < dep[top[v]] ? v = fath[top[v]] : u = fath[top[u]];
            return dep[u] < dep[v] ? u : v;
        }
    }
    
    namespace Splay{
    	#define lson son[now_][0]
    	#define rson son[now_][1]
    	#define f fa[now_]
    	int root, fa[MAXN], son[MAXN][2], node_num = 1;
    	int val[MAXN], cnt[MAXN], siz[MAXN];
    	int top = 0, bin[MAXN];
    	void Push_up(int now_){
    		siz[now_] = cnt[now_];
    		if(lson) siz[now_] += siz[lson];
    		if(rson) siz[now_] += siz[rson]; 
    	}
    	void Clear(int now_){
    		lson = rson = f = val[now_] = cnt[now_] = siz[now_] = 0;
    		bin[++top] = now_;
    	}
    	int NewNode(int fa_, int val_){
    		int now_ = (top ? bin[top--] : ++node_num);
    		fa[now_] = fa_;
    		val[now_] = val_;
    		siz[now_] = cnt[now_] = 1;
    		return now_; 
    	}
    	int Whichson(int now_) { return now_ == son[f][1]; }
    	void Rotate(int now_){
    		int fa_ = f, w = Whichson(now_);
    		if(fa[f]) son[fa[f]][Whichson(fa_)] = now_;
    		fa[now_] = fa[f];
    		
    		son[fa_][w] = son[now_][w ^ 1];
    		fa[son[fa_][w]] = fa_;
    		
    		son[now_][w ^ 1] = fa_;
    		fa[fa_] = now_;
    		Push_up(fa_), Push_up(now_); 
    	}
    	void Splay(int now_){
    		for( ; f; Rotate(now_)){
    			if(fa[f]) Rotate(Whichson(now_) == Whichson(f) ? f : now_);
    		}
    		root = now_;
    	}
    	void Insert(int now_, int fa_, int val_){
    		if(now_ && val[now_] != val_){
    			Insert(son[now_][val[now_] < val_], now_, val_);
    			return ;
    		}
    		if(val[now_] == val_) ++ cnt[now_];
    		if(!now_){
    			now_ = NewNode(fa_, val_);
    			if(f) son[f][val[f] < val_] = now_;
    		}
    		Push_up(now_), Push_up(f), Splay(now_);
    	}
    	int Find(int now_, int val_){
    		if(!now_) return false;
    		if(val_ < val[now_]) return Find(son[now_][0], val_);
    		if(val_ == val[now_]){
    			Splay(now_);
    			return true;
    		}
    		return Find(rson, val_);
    	}
    	void Delete(int val_){
    		if(!Find(root, val_)) return ;
    		if(cnt[root] > 1) {
    			-- cnt[root];
    			Push_up(root);
    			return ;
    		}
    		int oldroot = root;
    		if(!son[root][0] && !son[root][1]){
    			root = 0;
    		} else if(!son[root][0]){
    			root = son[root][1], fa[root] = 0;
    		} else if(!son[root][1]){
    			root = son[root][0], fa[root] = 0;
    		} else if(son[root][0] && son[root][1]){
    			int leftmax = son[root][0];
    			while(son[leftmax][1]) leftmax = son[leftmax][1];
    			Splay(leftmax);
    			son[root][1] = son[oldroot][1], fa[son[root][1]] = root;
    		}
    		Clear(oldroot), Push_up(root);
    	}
    	int QueryRank(int val_){
    		Insert(root, 0, val_);
    		int ret = siz[son[root][0]] + 1;
    		Delete(val_);
    		return ret;
    	}
    	int QueryVal(int rk_){
    		int now_ = root;
    		while(true){
    			if(!now_) return -1;
    			if(lson && siz[lson] >= rk_){
    				now_ = lson;
    			} else{
    				rk_ -= lson ? siz[lson] : 0;
    				if(rk_ <= cnt[now_]){
    					return val[now_];
    				}
    				rk_ -= cnt[now_];
    				now_ = rson;
    			}
    		}
    	}
    	int QueryPre(int val_){
    		Insert(root, 0, val_);
    		int now_ = son[root][0];
    		while(rson) now_ = rson;
    		Delete(val_);
    		return val[now_];
    	}
    	int QueryNext(int val_){
    		Insert(root, 0, val_);
    		int now_ = son[root][1];
    		while(lson) now_ = lson;
    		Delete(val_);
    		return val[now_];
    	}
    }
    using namespace Splay;
    using namespace Cut;
    
    int Get_dis(int u, int v) { return dis[u] + dis[v] - 2 * dis[Get_Lca(u, v)]; }
    
    signed main()
    {
        n = read();
        for(int i = 1, u, v, w; i < n; ++i) {
            u = read(), v = read(), w = read();
            add_edge(u, v, w), add_edge(v, u, w);
        }
        dfs(1, 0), dfs2(1, 1);
        m = read();
        char ch;
        for(int i = 1, x, l, r; i <= m; ++i) {
            cin >> ch;
            if(ch == '+') {
                x = read();
                l = pre[QueryPre(dfn[x])];
                r = pre[QueryNext(dfn[x])];
                if(l) Ans += Get_dis(l, x);
                if(r) Ans += Get_dis(x, r);
                if(l && r) Ans -= Get_dis(l, r);
    //            Ans += 2 * dis[Get_Lca(l, r)] + dis[x] - 2 * dis[Get_Lca(l, x)] + dis[x] - 2 * dis[Get_Lca(x, r)];           
                Insert(root, 0, dfn[x]);
            } else if(ch == '-') {
                x = read();
                l = pre[QueryPre(dfn[x])];
                r = pre[QueryNext(dfn[x])];
                if(l) Ans -= Get_dis(l, x);
                if(r) Ans -= Get_dis(x, r);
                if(l && r) Ans += Get_dis(l, r);
    //            Ans -= (2 * dis[Get_Lca(l, r)] + dis[x] - 2 * dis[Get_Lca(l, x)] + dis[x] - 2 * dis[Get_Lca(x, r)]);
                Delete(dfn[x]);
            } else {
    //            if(Cnt == 1) printf("0\n");
    //            else printf("%lld\n", Ans / 2);
                l = pre[QueryVal(1)], r = pre[QueryVal(Cnt)]; // 我选择最后处理序列两端点
                int Res = Ans; //记个变量暂时存储答案
                if(l && r) Res += Get_dis(l, r);
                printf("%lld\n", Res / 2);
            }
        }
        return 0;
    }
    

    鸣谢

    • 《信息学奥赛一本通》
  • 相关阅读:
    Code Chef December Challenge 2018题解
    UOJ#419. 【集训队作业2018】圆形(格林公式)
    BZOJ2178: 圆的面积并(格林公式)
    LOJ#3052. 「十二省联考 2019」春节十二响(启发式合并)
    LOJ#3048. 「十二省联考 2019」异或粽子(trie树+堆)
    Code Chef MINPOLY(计算几何+dp)
    LOJ#3088. 「GXOI / GZOI2019」旧词(树剖+线段树)
    LOJ#3087. 「GXOI / GZOI2019」旅行者(最短路)
    P5816 [CQOI2010]内部白点
    P5590 赛车游戏
  • 原文地址:https://www.cnblogs.com/Silymtics/p/14514629.html
Copyright © 2020-2023  润新知