【LCA】HDU 5296 Annoying problem

通道:http://acm.hdu.edu.cn/showproblem.php?pid=5296

题意:给一棵n个节点的树,再给q个操作,初始集合S为空,每个操作要在一个集合S中删除或增加某些点,输出每次操作后:要使得集合中任意两点互可达所耗最小需要多少权值。(记住只能利用原来给的树边。给的树边已经有向。10万个点,10万个操作)

思路:

代码:

  1 #pragma comment(linker, "/STACK:102400000,102400000")
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <set>
  6 #include <cmath>
  7
  8 using namespace std;
  9
 10 const int MAX_N = 200007;
 11
 12 struct  Node {
 13     int v, w, nxt;
 14     Node () {
 15
 16     }
 17     Node (int _v, int _w, int _n) {
 18         v = _v;
 19         w = _w;
 20         nxt = _n;
 21     }
 22 };
 23
 24 int head[MAX_N], edgecnt, now;
 25 int dfn[MAX_N], id[MAX_N << 1], dep[MAX_N << 1];
 26 int dis[MAX_N], dp[MAX_N << 1][24];;
 27 Node G[MAX_N << 2];
 28
 29 void Clear() {
 30     edgecnt = 0, now = 0;
 31     memset(head, -1, sizeof head);
 32     memset(dis, 0, sizeof dis);
 33 }
 34
 35 void add(int u, int v, int w) {
 36     G[edgecnt] = Node(v, w, head[u]);
 37     head[u] = edgecnt++;
 38 }
 39
 40 int pa[23][MAX_N];
 41
 42 void dfs(int u, int fa, int d) {
 43     dep[u] = d;
 44     pa[0][u] = fa;
 45     dfn[u] = now, id[now++] = u;
 46     for (int i = head[u]; ~i; i = G[i].nxt) {
 47         int v = G[i].v;
 48         if (v != fa) {
 49             dis[v] = dis[u] + G[i].w;
 50             dfs(v, u, d + 1);
 51         }
 52     }
 53 }
 54
 55 int lca(int u, int v) {
 56     if (dep[u] > dep[v]) swap(u, v);
 57     for (int k = 0; k < 23; ++k) {
 58         if ((dep[v] - dep[u]) >> k & 1) {
 59             v = pa[k][v];
 60         }
 61     }
 62     if (u == v) return u;
 63     for (int k = 23 - 1; k >= 0; --k) {
 64         if (pa[k][v] != pa[k][u]) {
 65             v = pa[k][v];
 66             u = pa[k][u];
 67         }
 68     }
 69     return pa[0][u];
 70 }
 71
 72 set<int> s;
 73 int n, m;
 74
 75 int cal(int u) {
 76     if (s.empty()) return 0;
 77     set<int>::iterator it = s.lower_bound(u);
 78     int y = *it; --it; int x = *it;
 79     x = id[x], y = id[y];
 80     int l = *s.begin(), r = *s.rbegin();
 81     if (u < l || r < u)    x = id[l], y = id[r];
 82     u = id[u];
 83     return dis[u] - dis[lca(x, u)] - dis[lca(y, u)] + dis[lca(x, y)];
 84 }
 85
 86 int main() {
 87     int T, cas = 0;
 88     scanf("%d", &T);
 89     while (T-- > 0) {
 90         Clear();
 91         s.clear();
 92         scanf("%d%d", &n, &m);
 93         for (int i = 1; i < n; ++i) {
 94             int u, v, w;
 95             scanf("%d%d%d", &u, &v, &w);
 96             add(u, v, w), add(v, u, w);
 97         }
 98         dfs(1, -1, 0);
 99         for (int k = 0; k + 1 < 23; ++k) {
100             for (int v = 1; v <= n; ++v) {
101                 if (pa[k][v] < 0) pa[k + 1][v] = -1;
102                 else pa[k + 1][v] = pa[k][pa[k][v]];
103             }
104         }
105         printf("Case #%d:\n", ++cas);
106         int ans = 0;
107         for (int i = 0; i < m; ++i) {
108             int t, v;
109             scanf("%d%d", &t, &v);
110             v = dfn[v];
111             if (1 == t) {
112                 if (!s.count(v)) {
113                     ans += cal(v);
114                     s.insert(v);
115                 }
116             } else {
117                 if (s.count(v)) {
118                     s.erase(v);
119                     ans -= cal(v);
120                 }
121             }
122             printf("%d\n", ans);
123         }
124     }
125     return 0;
126 }

时间: 2024-10-13 05:14:50

【LCA】HDU 5296 Annoying problem的相关文章

HDU 5296 Annoying Problem 树链剖分 LCA 倍增法

HDU 5296 Annoying Problem 题目链接:hdu 5296 题意:在一棵给定的具有边权的树,一个节点的集合S(初始为空),给定Q个操作,每个操作增加或删除S中的一个点,每个操作之后输出使集合S中所有点联通的最小子树的边权和. 思路:最小子树上的节点的充要条件: 节点为(S集合中所有点的LCA)的子节点: 节点有一个子孙为S集合中的点. 那么我们给每个节点都开一个标记数组,初始为零,每加入一个节点,就把从这个节点到根节点路径上的点的值都+1,反之-1,这样通过对每个单节点值的查

HDU 5296 Annoying problem(LCA模板+树的dfs序心得)

Problem Description Coco has a tree, whose nodes are conveniently labeled by 1,2,-,n, which has n-1 edge,each edge has a weight. An existing set S is initially empty. Now there are two kinds of operation: 1 x: If the node x is not in the set S, add n

[HDU 5296] Annoying problem (DFS序性质+LCA)

HDU - 5296 一棵树上有若干个点,每条边有一个边权 给一个初始为空的集合,每次向集合内添加一个点或者删除一个点 问每次操作结束后,将集合内所有点连起来的边权和为多少 假设集合内已经有一些点,那么再加一个点所增加的边权 将会是这个点到某一条链的距离 但是这条链不能随便挑选,否则可能会经过已经选择的边 挑选策略就是,找到集合内dfs序比当前点大和小的两个点组成的链 换句话来说,就是集合内dfs序最靠近当前点的两个点 设当前点 x,这样选链,选出的链是 (u,v),并且设dfsn[u]<dfs

HDU 5296 Annoying problem LCA+树状数组

题解链接 Annoying problem Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 480    Accepted Submission(s): 146 Problem Description Coco has a tree, whose nodes are conveniently labeled by 1,2,-,n, w

HDU 5296 Annoying problem

http://acm.hdu.edu.cn/showproblem.php?pid=5296 实际上问题可以转化为点到一个连通子树的最短距离.. //Hello. I'm Peter. #pragma comment(linker, "/STACK:102400000,102400000") #include<cstdio> #include<iostream> #include<sstream> #include<cstring> #i

【转】HDU 3199 Hamming Problem: 规律打表

应该不是数论···逻辑思维?找规律?暂且放到支个分类下··· 我的理解: 数组 num表 保存 只有P1 P2 or P3的因子的 常数 并按递增顺序. 通过已有的常数 每次循环相应的乘以P1 P2 P3, 就保证了表中的 常数 的因子排他性. 也可以 手推+程序显示 探究其规律. #include<iostream> using namespace std; const int Size=100000; long long num[Size]; int main() { long long

【multiset】hdu 5349 MZL&#39;s simple problem

[multiset]hdu 5349 MZL's simple problem 题目链接:hdu 5349 MZL's simple problem 题目大意 n次操作,插入元素.删除最小元素.查询最大元素并输出. C++STL的multiset的使用 set--多元集合(元素不可重复),multiset--可重复元素的多元集合 多元集合(MultiSets)和集合(Sets)相像,只不过支持重复对象.(具体用法请参照set容器) set和multiset内部是以平衡二叉树实现的: 从内部数据结

【dfs】hdu 1016 Prime Ring Problem

[dfs]hdu 1016 Prime Ring Problem 题目链接 刚开始接触搜索,先来一道基本题目练练手. 注意对树的深度进行dfs dfs过程中注意回退!!! 素数提前打表判断快一些 参考代码 /*Author:Hacker_vision*/ #include<bits/stdc++.h> #define clr(k,v) memset(k,v,sizeof(k)) using namespace std; const int _max=1e3+10;//素数打表 int n,pr

矩阵十题【八】 HDU 1715 A Simple Math Problem

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1757 题目大意: If x < 10   ,则  f(x) = x. If x >= 10 ,则  f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + -- + a9 * f(x-10); 给出k,m和a0~a9,求f(k)%m,  k<2*10^9 , m < 10^5 这是一个递推式,故可以用矩阵乘法来求 和上题类似,具体思路过程见上题