HDU 5458 Stability (树链剖分+并查集+set)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5458

给你n个点,m条边,q个操作,操作1是删边,操作2是问u到v之间的割边有多少条。

这题要倒着做才容易,倒着加边。

因为这题最后保证所有的点一定连通,所以可以构建一棵树,树链剖分一下。要是u到v之间只有一条边,那便是一条割边。而加边的话,就是将u到v之间的边权都+1,边权等于1的话是桥,大于1的话就不是了。所以我们初始化树的时候,可以将边权初始化为1,加边操作将边权赋值为0。求u到v的割边个数的话,就是求u到v的和。

中间存边我用multiset( map 超时),然后用并查集判断是不是一棵完整的树从而看情况加边。

  1 //#pragma comment(linker, "/STACK:102400000, 102400000")
  2 #include <algorithm>
  3 #include <iostream>
  4 #include <cstdlib>
  5 #include <cstring>
  6 #include <cstdio>
  7 #include <vector>
  8 #include <cmath>
  9 #include <ctime>
 10 #include <list>
 11 #include <set>
 12 #include <map>
 13 using namespace std;
 14 typedef long long LL;
 15 typedef pair <int, int> P;
 16 const int N = 1e5 + 5;
 17 struct Edge {
 18     int next, to;
 19 }edge[N << 1];
 20 multiset <P> myset; //存 构建的树的边
 21 int head[N], tot;
 22 int pre[N], son[N], dep[N], size[N], cnt; //son:重儿子
 23 int top[N], id[N];
 24 int uu[N], vv[N], c[N]; //q个操作c uu vv
 25 int x[N], y[N]; //开始输入的m条边x y
 26 int ans[N]; //答案
 27 int par[N]; //并查集
 28
 29 void init(int n) {
 30     for(int i = 1; i <= n; ++i) {
 31         par[i] = i;
 32         head[i] = -1;
 33     }
 34     tot = cnt = 0;
 35     myset.clear();
 36 }
 37
 38 inline void add(int u, int v) {
 39     edge[tot].next = head[u];
 40     edge[tot].to = v;
 41     head[u] = tot++;
 42 }
 43
 44 inline int search(int n) {
 45     if(n == par[n])
 46         return n;
 47     return par[n] = search(par[n]);
 48 }
 49
 50 void dfs1(int u, int p, int d) {
 51     dep[u] = d, size[u] = 1, son[u] = u, pre[u] = p;
 52     for(int i = head[u]; ~i; i = edge[i].next) {
 53         int v = edge[i].to;
 54         if(v == p)
 55             continue;
 56         dfs1(v, u, d + 1);
 57         if(size[v] >= size[son[u]])
 58             son[u] = v;
 59         size[u] += size[v];
 60     }
 61 }
 62
 63 void dfs2(int u, int p, int t) {
 64     top[u] = t, id[u] = ++cnt;
 65     if(son[u] != u)
 66         dfs2(son[u], u, t);
 67     for(int i = head[u]; ~i; i = edge[i].next) {
 68         int v = edge[i].to;
 69         if(v == p || v == son[u])
 70             continue;
 71         dfs2(v, u, v);
 72     }
 73 }
 74
 75 struct SegTree {
 76     int l, r, val, lazy, mid;
 77 }T[N << 2];
 78
 79 void build(int p, int l, int r) {
 80     T[p].mid = (l + r) >> 1;
 81     T[p].l = l, T[p].r = r, T[p].lazy = 1;
 82     if(l == r) {
 83         T[p].val = 1;
 84         return ;
 85     }
 86     build(p << 1, l, T[p].mid);
 87     build((p << 1)|1, T[p].mid + 1, r);
 88     T[p].val = T[p << 1].val + T[(p << 1)|1].val;
 89 }
 90
 91 inline void pushdown(int p) {
 92     int ls = p << 1, rs = (p << 1)|1;
 93     T[ls].lazy = T[rs].lazy = T[ls].val = T[rs].val = 0;
 94     T[p].lazy = 1;
 95 }
 96
 97 void update(int p, int l, int r) {
 98     if(T[p].val == 0)
 99         return ;
100     if(T[p].l == l && T[p].r == r) {
101         T[p].lazy = T[p].val = 0;
102         return ;
103     }
104     if(!T[p].lazy) {
105         pushdown(p);
106     }
107     if(r <= T[p].mid) {
108         update(p << 1, l, r);
109     }
110     else if(l > T[p].mid) {
111         update((p << 1)|1, l, r);
112     }
113     else {
114         update(p << 1, l, T[p].mid);
115         update((p << 1)|1, T[p].mid + 1, r);
116     }
117     T[p].val = T[p << 1].val + T[(p << 1)|1].val;
118 }
119
120 int query(int p, int l, int r) {
121     if(T[p].val == 0)
122         return 0;
123     if(T[p].l == l && T[p].r == r) {
124         return T[p].val;
125     }
126     if(!T[p].lazy) {
127         pushdown(p);
128     }
129     if(r <= T[p].mid) {
130          return query(p << 1 , l , r);
131      }
132      else if(l > T[p].mid) {
133          return query((p << 1)|1 , l , r);
134      }
135      else {
136          return query(p << 1 , l , T[p].mid) + query((p << 1)|1 , T[p].mid + 1 , r);
137      }
138 }
139
140 void change(int u, int v) {
141     int fu = top[u], fv = top[v];
142     while(fu != fv) {
143         if(dep[fu] >= dep[fv]) {
144             update(1, id[fu], id[u]);
145             u = pre[fu];
146             fu = top[u];
147         }
148         else {
149             update(1, id[fv], id[v]);
150             v = pre[fv];
151             fv = top[v];
152         }
153     }
154     if(u == v)
155         return ;
156     else if(dep[u] >= dep[v])
157         update(1, id[son[v]], id[u]);
158     else
159         update(1, id[son[u]], id[v]);
160 }
161
162 int find(int u, int v) {
163     int fu = top[u], fv = top[v], res = 0;
164     while(fu != fv) {
165         if(dep[fu] >= dep[fv]) {
166             res += query(1, id[fu], id[u]);
167             u = pre[fu];
168             fu = top[u];
169         }
170         else {
171             res += query(1, id[fv], id[v]);
172             v = pre[fv];
173             fv = top[v];
174         }
175     }
176     if(u == v)
177         return res;
178     else if(dep[u] >= dep[v])
179         return res + query(1, id[son[v]], id[u]);
180     else
181         return res + query(1, id[son[u]], id[v]);
182 }
183
184 int main()
185 {
186     int t, n, m, q, u, v;
187     scanf("%d", &t);
188     for(int ca = 1; ca <= t; ++ca) {
189         scanf("%d %d %d", &n, &m, &q);
190         init(n);
191         for(int i = 1; i <= m; ++i) {
192             scanf("%d %d", x + i, y + i);
193             if(x[i] > y[i])
194                 swap(x[i], y[i]);
195             myset.insert(make_pair(x[i], y[i])); //先存边
196         }
197         for(int i = 1; i <= q; ++i) {
198             scanf("%d %d %d", c + i, uu + i, vv + i);
199             if(uu[i] > vv[i])
200                 swap(uu[i], vv[i]);
201             if(c[i] == 1) {
202                 auto pos = myset.find(make_pair(uu[i], vv[i])); //去除要删的边
203                 myset.erase(pos);
204             }
205         }
206         int f = 0;
207         for(auto i = myset.begin(); i != myset.end(); ++i) {
208             u = search(i->first), v = search(i->second);
209             if(u == v) { //不成环
210                 ++f;
211                 x[f] = i->first, y[f] = i->second; //此时的x y存的是非树也非删的边
212                 continue;
213             }
214             par[u] = v;
215             add(i->first, i->second);
216             add(i->second, i->first);
217         }
218         dfs1(1, -1, 0);
219         dfs2(1, -1, 1);
220         printf("Case #%d:\n", ca);
221         build(1, 2, n);
222         for(int i = 1; i <= f; ++i) {
223             change(x[i], y[i]);
224         }
225         f = 0;
226         for(int i = q; i >= 1; --i) {
227             if(c[i] == 1) {
228                 change(uu[i], vv[i]);
229             }
230             else {
231                 ans[++f] = find(uu[i], vv[i]);
232             }
233         }
234         for(int i = f; i >= 1; --i) {
235             printf("%d\n", ans[i]);
236         }
237     }
238     return 0;
239 }

写的我比较头痛,调试了比较久...

时间: 2024-08-03 10:19:37

HDU 5458 Stability (树链剖分+并查集+set)的相关文章

hdu 5458 Stability(树链剖分+并查集)

Stability Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 65535/102400 K (Java/Others)Total Submission(s): 1347    Accepted Submission(s): 319 Problem Description Given an undirected connected graph G with n nodes and m edges, with possibly r

bzoj3694: 最短路(树链剖分/并查集)

bzoj1576的帮我们跑好最短路版本23333(双倍经验!嘿嘿嘿 这题可以用树链剖分或并查集写.树链剖分非常显然,并查集的写法比较妙,涨了个姿势,原来并查集的路径压缩还能这么用... 首先对于不在最短路径树上的边x->y,设t为最短路径树上lca(x,y),则t到y上的路径上的点i到根的距离都可以用h[x]+dis[x][y]+h[y]-h[i](h[]为深度)来更新,因为h[i]一定,只要让h[x]+dis[x][y]+h[y]最小就行,这里用树剖直接修改整条链上的数,就可以过了. 并查集的

HDU 3966 RE 树链剖分 Aragorn&#39;s Story

给一棵点带权的图 有这样一个操作: 使树上某一条路径所有点权值增减 每次询问某个点现在的权值. 树链剖分完以后,就是线段树的成段更新了. 这题感觉A不了了,无限RE,手动开栈也没卵用. 还是把我辛辛苦苦写的代码贴一下吧. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 using

HDU 5044 (树链剖分+树状数组+点/边改查)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5044 题目大意:修改链上点,修改链上的边.查询所有点,查询所有边. 解题思路: 2014上海网赛的变态树链剖分模板题.将以往树链剖分的点&边修改和查询合在一起之后,难度上去不少. 第一个卡人点是读入优化. 第二个卡人点是树状数组.由于要查询所有点,如果使用线段树,每次都要扫到底层才能取出点值,必T无疑. 然后使用树状数组之后,树链剖分的点/边修改写法有些变动. 点查询变化不大. 边查询只要查询一下

HDU 3966(树链剖分+点修改+点查询)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966 题目大意:营地的分布成树型.每个营地都有一些人,每次修改修改一条链上的所有营地的人数,每次查询单个点. 解题思路: 树链剖分基础题. 维护一个sum. 注意轻链修改时,点修改和边修改的不同. 由于树的结构与线段树点的顺序不太相同,因此需要做一个映射数组rank.故在线段树Build的时候,权值是camp[rank[l]],rank这步的映射在dfs2的时候完成,rank[w[u]]=u; Qu

HDU 5044 Tree 树链剖分

一棵树,初始边权和点权都为0 现在有m个操作,每一个操作: ADD1 u v k: for nodes on the path from u to v, the value of these nodes increase by k. ADD2 u v k: for edges on the path from u to v, the value of these edges increase by k. 操作完后,输出每一个点的点权和每一条边的边权(边权按照输入顺序输出) 我们把边权也当做点权处

HDU 5242 利用树链剖分思想进行贪心

题目大意: 在给定带权值节点的树上从1开始不回头走到某个底端点后得到所有经过的点的权值后,这些点权值修改为0,到达底部后重新回到1,继续走,问走k次,最多能得到多少权值之和 这其实就是相当于每一次都走权值最大的那一条路径,进行贪心k次 首先先来想想树链剖分的时候的思想: 重儿子表示这个儿子对应的子树的节点数最多,那么每次访问都优先访问重儿子 这道题里面我们进行一下转化,如果当前儿子能走出一条最长的路径,我们就令其为重儿子,那么很容易想到,到达父亲时,如果选择重儿子,那么之前到达 父亲所得的权值一

HDU 3966 基础树链剖分

题意:给一棵树,并给定各个点权的值,然后有3种操作:I C1 C2 K: 把C1与C2的路径上的所有点权值加上KD C1 C2 K:把C1与C2的路径上的所有点权值减去KQ C:查询节点编号为C的权值 1 #pragma comment(linker, "/STACK:1024000000,1024000000") 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #in

Hdu 5458 Stability (LCA + 并查集 + 树状数组 + 缩点)

题目链接: Hdu 5458 Stability 题目描述: 给出一个还有环和重边的图G,对图G有两种操作: 1 u v, 删除u与v之间的一天边 (保证这个边一定存在) 2 u v, 查询u到v的路径上有几条桥. 解题思路: 这个题目有很多次操作,包含查询和删边两类,首先想到的是连通分量加缩点.如果按照顺序来,删边时候求桥就是问题了.所以可以离线处理,然后一边记录答案一边加边缩点. 对于一个图,把连通分量缩成一个点后,这个图就成为了一棵树, 然后深度差就等于桥的数目.查询的时候对于(u, v)