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

题目链接:

  Hdu 5458 Stability

题目描述:

  给出一个还有环和重边的图G,对图G有两种操作:

  1 u v, 删除u与v之间的一天边 (保证这个边一定存在)

  2 u v, 查询u到v的路径上有几条桥。

解题思路:

  这个题目有很多次操作,包含查询和删边两类,首先想到的是连通分量加缩点。如果按照顺序来,删边时候求桥就是问题了。所以可以离线处理,然后一边记录答案一边加边缩点。

  对于一个图,把连通分量缩成一个点后,这个图就成为了一棵树, 然后深度差就等于桥的数目。查询的时候对于(u, v)桥的数目等于(设定deep[x]为x的深度,LCA(x, y)为(x, y)的父节点) deep[u] + deep[v] - 2 * deep[LCA(u, v)];

  现在问题就成了怎么才能更快的缩点和维护点的深度。

  缩点问题:在树上加上一条边就会形成一个环,然后这个环(强连通分量)上点就会被缩成一个点,然后用并查集来维护强联通分量,每次只需要合并父节点(每个点只能被缩一次,复杂度被平摊还是很可观的)。

  维护节点深度:每次儿子节点合并到父亲节点的时候,儿子节点的子孙节点的深度都要减一。在这里我们可以用dfs序 + 树状数组来维护节点的深度。利用树状数组的区间更改点查询优化维护深度的操作。

  还有最后一个问题就是计算(u, v)节点之间桥数目的时候,需要用到LCA,可以利用树上倍增LCA来优化时间复杂度。

这个题目还是很值得本扎扎练手的,考察了很多东西,真是把压箱底的东西都翻出来晒了晒,仰慕当场ac的聚聚们。

  1 #include <vector>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <iostream>
  5 #include <algorithm>
  6 using namespace std;
  7 const int maxn = 30010;
  8 const int N = 100010;
  9 const int M = 16;
 10
 11 struct node
 12 {
 13     int to, del;
 14     node(int x):to(x),del(0) {}
 15     bool operator < (const node & a) const
 16     {
 17         if (to != a.to) return to < a.to;
 18         return del > a.del;
 19     }
 20 };
 21 vector <node> vec[maxn];
 22
 23 struct Query
 24 {
 25     int op, u, v;
 26     void read ()
 27     {
 28         scanf ("%d %d %d", &op, &u, &v);
 29         if (op == 1)
 30         {
 31             lower_bound (vec[u].begin(), vec[u].end(), node(v)) -> del = true;
 32             lower_bound (vec[v].begin(), vec[v].end(), node(u)) -> del = true;
 33         }
 34     }
 35 } query[N];
 36
 37 int n, m, q;
 38
 39 void init ()
 40 {
 41     for (int i=0; i<=n; i++)
 42         vec[i].clear();
 43 }
 44
 45 struct bit_tree
 46 {
 47     int tree[maxn];
 48
 49     void init()
 50     {
 51         memset (tree, 0, sizeof(tree));
 52     }
 53
 54     int lowbit (int x)
 55     {
 56         return x & (-x);
 57     }
 58
 59     void change (int x, int num)
 60     {
 61         while (x <= n)
 62         {
 63             tree[x] += num;
 64             x += lowbit (x);
 65         }
 66     }
 67
 68     void update (int x, int y, int num)
 69     {
 70         change (x, num);
 71         change (y+1, -num);
 72     }
 73
 74     int query (int x)
 75     {
 76         int res = 0;
 77         while (x)
 78         {
 79             res += tree[x];
 80             x -= lowbit (x);
 81         }
 82         return res;
 83     }
 84
 85 }Bit_tree;
 86
 87 int begin_id[maxn], end_id[maxn], deep[maxn], ntime;
 88 int fa[maxn][M];
 89 void dfs (int u, int father, int dep)
 90 {
 91     if (father > 0)
 92         vec[u].erase (lower_bound(vec[u].begin(), vec[u].end(), node(father)));
 93
 94     fa[u][0] = father;
 95     begin_id[u] = ++ntime;
 96     deep[u] = dep;
 97
 98     for (int i=0; i<vec[u].size(); i++)
 99     {
100         node p = vec[u][i];
101         if (p.del || begin_id[p.to]) continue;
102         vec[u][i].del = true;
103         dfs (p.to, u, dep+1);
104     }
105
106     end_id[u] = ntime;
107     Bit_tree.update (begin_id[u], end_id[u], 1);
108 }
109
110 struct Lca
111 {
112     void init ()
113     {
114         for (int i=1; i<M; i++)
115             for (int j=1; j<=n; j++)
116                 fa[j][i] = fa[fa[j][i-1]][i-1];
117     }
118
119     int lca (int u, int v)
120     {
121         if (deep[u] < deep[v])   swap (u, v);
122         int num = deep[u] - deep[v];
123         for (int i=0; i<M; i++)
124             if ((1<<i) & num)   u = fa[u][i];
125         if (u == v) return u;
126         for (int i=M-1; i>=0; i--)
127             if (fa[u][i] != fa[v][i])
128             {
129                 u = fa[u][i];
130                 v = fa[v][i];
131             }
132         return fa[u][0];
133     }
134
135 }LCA;
136
137 int father[maxn];
138 struct UNion
139 {
140     void init()
141     {
142         for (int i=0; i<=n; i++)
143             father[i] = i;
144     }
145
146     int find (int x)
147     {
148         if (x != father[x]) father[x] = find (father[x]);
149         return father[x];
150     }
151
152     void merge_fa (int x, int y)
153     {
154         x = find (x);
155         while (x != y)
156         {
157             int t = find(fa[x][0]);
158             father[x] = t;
159             Bit_tree.update(begin_id[x], end_id[x], -1);
160             x = t;
161         }
162     }
163
164     void merge (int x, int y)
165     {
166         int l = find (LCA.lca (x, y));
167         merge_fa (x, l);
168         merge_fa (y, l);
169     }
170
171 }Union;
172
173 int ans[N], res;
174 void solve ()
175 {
176     Bit_tree.init ();
177     ntime = res = 0;
178     memset (begin_id, 0, sizeof(begin_id));
179     dfs (1, 0, 1);
180     LCA.init ();
181     Union.init();
182
183     for (int i=1; i<=n; i++)
184     {
185         for (int j=0; j<vec[i].size(); j++)
186         {
187             node p = vec[i][j];
188             if (p.del)  continue;
189             Union.merge (p.to, i);
190         }
191     }
192
193     for (int i=q; i>0; i--)
194     {
195         Query p = query[i];
196         int u = begin_id[p.u];
197         int v = begin_id[p.v];
198         int x = begin_id[LCA.lca(p.u, p.v)];
199         if (p.op == 1)
200             Union.merge(p.u, p.v);
201         else
202             ans[++ res] = Bit_tree.query(u) + Bit_tree.query(v) - 2*Bit_tree.query(x);
203     }
204
205     while(res)
206         printf ("%d\n", ans[res --]);
207 }
208
209 int main ()
210 {
211     int t, u, v;
212     scanf ("%d", &t);
213
214     for (int i=1; i<=t; i++)
215     {
216         scanf("%d %d %d", &n, &m, &q);
217         init ();
218
219         for (int j=0; j<m; j++)
220         {
221             scanf ("%d %d", &u, &v);
222             vec[u].push_back (node(v));
223             vec[v].push_back (node(u));
224         }
225
226         for (int j=1; j<=n; j++)
227             sort (vec[j].begin(), vec[j].end());
228
229         for (int j=1; j<=q; j++)
230             query[j].read();
231
232         printf ("Case #%d:\n", i);
233         solve ();
234
235     }
236     return 0;
237 }

  

时间: 2024-10-04 16:35:10

Hdu 5458 Stability (LCA + 并查集 + 树状数组 + 缩点)的相关文章

HDU 5458 Stability(双连通分量+LCA+并查集+树状数组)(2015 ACM/ICPC Asia Regional Shenyang Online)

题目大意:给一个N个点M条边的无向图,有Q个询问:1.删掉a.b之间所存在的边:2.询问有多少条边,单独删掉之后a与b不再连通. 思路:脑洞大开. 对于询问,首先想到的就是a与b之间有多少桥(割边),然后想到双连通分量,然而删边是个坑爹的问题,于是我们离线倒着来,把删边变成加边. 双连通分量这种东西呢,其实缩点连起来之后,就是一棵树辣. 然后询问两个点的时候,设根到点x的距离为dep[x],a.b的最近公共祖先为lca(a, b),那么询问query(a, b) = dep[a] + dep[b

【bzoj3211】花神游历各国 并查集+树状数组

原文地址:http://www.cnblogs.com/GXZlegend/p/6809714.html 题目描述 输入 输出 每次x=1时,每行一个整数,表示这次旅行的开心度 样例输入 4 1 100 5 5 5 1 1 2 2 1 2 1 1 2 2 2 3 1 1 4 样例输出 101 11 11 题解 并查集+树状数组,附带一点数学知识 因为√1=1,且一个数x开接近log2(log2x)次平方后就会变成1,这个数是非常小的. 所以我们完全可以暴力修改,只需要知道每次的修改区间中有多少真

BZOJ 3038 上帝造题的七分钟2 (并查集+树状数组)

题解:同 BZOJ 3211 花神游历各国,需要注意的是需要开long long,还有左右节点需要注意一下. #include <cstdio> #include <cmath> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; LL a[100005],c[100005]; int f[100005],n,m,op,l,r,t; int s

[BZOJ 3211]花神游历各国(并查集+树状数组)

Description Solution 树状数组单点修改区间查询 我们知道一个数n最多修改loglogn次就会变为1 并查集维护每个数右边第一个不为1的位置 #include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<cmath> #define MAXN 100005 using namespace std; typedef long lon

【BZOJ3211】【并查集+树状数组】花神游历各国

Description Input Output 每次x=1时,每行一个整数,表示这次旅行的开心度 Sample Input 4 1 100 5 5 5 1 1 2 2 1 2 1 1 2 2 2 3 1 1 4 Sample Output 101 11 11 [分析] 开始看一眼觉得线段树可做. 后来看题解用树状数组瞬秒......orzzz,注意到任何一个int都可以在很小的次数下变为1,所以直接暴力单点修改,将变成1的数parent设为它右边的数. 注意,输入中可能会有很多的0....直接

HDU 2227 Find the nondecreasing subsequences (DP+树状数组+离散化)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2227 Find the nondecreasing subsequences                                  Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)                                             

hdu 3584 二进制0,1反转 三维树状数组 及三维树状数组模板

先贴自己类比二维树状数组写的三维树状数组模板: 开始的时候循环体内j=y,k=z,没写,以为自己思路错了,,,hehe..... 更高维的树状数组以此类比 const int MAXN = 100+10; int c[MAXN][MAXN][MAXN];int X,Y,Z; int N; inline int lowbit(int x){return x&(-x);} void update(int x, int y, int z, int v) { int i=x,j=y,k=z; while

HDU 4417 类似求逆序数的树状数组

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2250    Accepted Submission(s): 1092 Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping ability

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include