• 【AW355】异象石


    题目链接:https://www.acwing.com/problem/content/357/

    题目大意:给定一棵树 , 要求实现以下三种操作: 将某个点进行标记, 将某个点撤销标记 , 求出连接所有标记的点的最短路径

    solution

    笔者通过查阅他人的Blog , 了解到这道题有一个有趣的性质 , 即把所有有异象石的点按照 dfs 序排序并围成一圈 , 相邻两个节点的距离之和即为询问答案的两倍 , 但均未给出证明 , 经过思考后 , 以下是笔者乱搞出的一种证明 , 不感兴趣的可以跳过:

    1.相邻两个节点的路径一定在连接所有标记的点的最短路径中 , 这很好证明 , 在树上 , 两个点之间的路径是唯一的 , 连接所有标记的点的最短路径必然包括 dfs 序相邻两个点的路径

    2.连接所有标记的点的最短路径一定都被连接相邻两个节点的路径所包含 , 这也很好证明 , 连接相邻两个节点的路径已经将这些标记点都联通了 , 既然是连接所有标记的点的最短路径 , 一定不会比连接相邻两个节点的路径再多

    由上可知 , 所有连接相邻两个节点的路径必然是连接所有标记的点的最短路径 , 且包含了所有路径 , 并且可以把它看做是从 dfs 序出发 , 遍历树上其它标记点 , 再回到起点 , 由树的性质可知 , 每条路径一定走过两遍 , 因此 , 相邻两个节点的距离之和即为询问答案的两倍 , 得证

    有了这个结论以后 , 之后就好办了 , 可以用平衡树或 set 之类的进行维护 , 如果插入一个点 (x_i) , 那么找到它相邻的两个点 (l_i) , (r_i) , 询问答案的两倍 (res) 就加上 (dist(l_i , x_i)+dist(x_i , r_i)-dist(l_i , r_i)) , 如果删除一个点 (x_i) , 那么找到它相邻的两个点 (l_i) , (r_i) , 询问答案的两倍 (res) 就减去 (dist(l_i , x_i)+dist(x_i , r_i)-dist(l_i , r_i)) , 如果进行询问 , 那么就输出 (res / 2)

    时间复杂度 : (O(mlogn))

    code

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T> inline void read(T &FF) {
    	int RR = 1; FF = 0; char CH = getchar();
    	for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
    	for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
    	FF *= RR;
    }
    inline void file(string str) {
    	freopen((str + ".in").c_str(), "r", stdin);
    	freopen((str + ".out").c_str(), "w", stdout);
    }
    #define int long long
    const int N = 2e5, Log = 21;
    int n, m, fa[N][Log + 1], dep[N], ans, dis[N];
    int now, si, fst[N], nxt[N], num[N], wi[N], dfn[N];
    void add(int u, int v, int w) {
    	nxt[++now] = fst[u], fst[u] = now, num[now] = v, wi[now] = w;
    	nxt[++now] = fst[v], fst[v] = now, num[now] = u, wi[now] = w;
    }
    void pre_lca(int xi) {
    	dep[xi] = dep[fa[xi][0]] + 1; dfn[xi] = ++si;
    	for(int i = 1; i <= Log; i++)
    		fa[xi][i] = fa[fa[xi][i - 1]][i - 1];
    	for(int i = fst[xi]; i; i = nxt[i])
    		if(num[i] != fa[xi][0])
    			dis[num[i]] = dis[xi] + wi[i], fa[num[i]][0] = xi, pre_lca(num[i]);
    }
    int lca(int xi, int yi) {
    	if(dep[xi] < dep[yi]) swap(xi, yi);
    	for(int i = Log; i >= 0; i--)
    		if(dep[fa[xi][i]] >= dep[yi])
    			xi = fa[xi][i];;
    	if(xi == yi) return xi;
    	for(int i = Log; i >= 0; i--)
    		if(fa[xi][i] != fa[yi][i])
    			xi = fa[xi][i], yi = fa[yi][i];
    	return fa[xi][0];
    }
    int dist(int xi, int yi) {
    	return  dis[xi] + dis[yi] - 2 * dis[lca(xi, yi)];
    }
    set<pair<int, int> > pi;
    signed main() {
    	//file("");
    	int u, v, w, xi; char op;
    	read(n);
    	for(int i = 1; i < n; i++)
    		read(u), read(v), read(w), add(u, v, w);
    	pre_lca(1);
    	read(m);
    	for(int i = 1; i <= m; i++) {
    		cin >> op;
    		if(op == '+') {
    			read(xi);
    			pi.insert(make_pair(dfn[xi], xi));
    			if(pi.size() > 1) {
    				set<pair<int, int> >::iterator qi = pi.find(make_pair(dfn[xi], xi)), pre = qi, succ = qi;
    				if(pre == pi.begin()) pre = (--pi.end()), succ++;
    				else if(succ == (--pi.end())) succ = pi.begin(), pre--;
    				else pre++, succ--;
    				int li = (*pre).second, ri = (*succ).second;
    				ans += dist(li, xi) + dist(xi, ri) - dist(li, ri);
    			}
    		}
    		else if(op == '-') {
    			read(xi);
    			if(pi.size() > 0) {
    				set<pair<int, int> >::iterator qi = pi.find(make_pair(dfn[xi], xi)), pre = qi, succ = qi;
    				if(pre == pi.begin()) pre = (--pi.end()), succ++;
    				else if(succ == (--pi.end())) succ = pi.begin(), pre--;
    				else pre++, succ--;
    				int li = (*pre).second, ri = (*succ).second;
    				ans -= dist(li, xi) + dist(xi, ri) - dist(li, ri);
    			}
    			pi.erase(make_pair(dfn[xi], xi));
    		}
    		else cout << ans / 2 << '
    ';
    	}
    	return 0;
    }
    
    
    
  • 相关阅读:
    Python模块:shutil、序列化(json&pickle&shelve)、xml
    IO流----操作文件的9种方法代码实现
    IO流--字符流与字节流--File类常用功能
    常用集合之间的关系
    HashMap 集合的遍历
    java代码实现ftp服务器的文件上传和下载
    java中数组、集合、字符串之间的转换,以及用加强for循环遍历
    java中的正则表达式
    使用Calender类获取系统时间和时间和运算
    产生任意区间的随机数
  • 原文地址:https://www.cnblogs.com/magicduck/p/12253804.html
Copyright © 2020-2023  润新知