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

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

Problem Description
Given an undirected connected graph G with n nodes and m edges, with possibly repeated edges and/or loops. The stability of connectedness between node u and node v is defined by the number of edges in this graph which determines the connectedness between them (once we delete this edge, node u and v would be disconnected).

You need to maintain the graph G, support the deletions of edges (though we guarantee the graph would always be connected), and answer the query of stability for two given nodes.
 
Input
There are multiple test cases(no more than 3 cases), and the first line contains an integer t, meaning the totally number of test cases.

For each test case, the first line contains three integers nm and q, where 1n3×104,1m105 and 1q105. The nodes in graph G are labelled from 1 to n.

Each of the following m lines contains two integers u and v describing an undirected edge between node u and node v.

Following q lines - each line describes an operation or a query in the formats:
 1 a b: delete one edge between a and b. We guarantee the existence of such edge.
 2 a b: query the stability between a and b.
 
Output
For each test case, you should print first the identifier of the test case.

Then for each query, print one line containing the stability between corresponding pair of nodes.

 

题目大意:给一个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] - 2 * dep[lca(a, b)]

加一条边的时候呢,比如加edge(a, b),那么原来的a到b的路径就形成了一个环,那么这个环就应该缩成一个双连通分量(每个点只会被缩一次,平摊的复杂度肯定是没问题的)。

这里可以用并查集来维护同一个双连通分量,每次都是儿子合并到父亲,然后每一次点u合并到父亲的时候,u和u的所有子孙的高度都会减一。

因为要处理一棵子树的所有值,这里使用DFS序+树状数组的方法来维护每个点的深度dep。

然后求LCA这里用的是树上倍增。整个题目要做的就是这些了。

 

然后整个流程就是:

1、初始化边表并读入所有数据并给边表排序用于查找(我这里用了vector)。(复杂度O(n+m+q+mlog(m)))

2、然后给1操作涉及的边打个删除标记。(复杂度O(qlog(m)))

3、DFS随便建一棵树,给DFS到的边打个删除标记(我直接把父边从vector移除了),顺便建立好DFS序和树状数组、dep和fa数组(用于LCA倍增)。(复杂度O(n+m+nlog(n)))

4、初始化LCA倍增。(复杂度O(nlog(n)))

5、对于每条没打标记的边(即树里的横向边?) edge(a, b),合并路径path(a, b)上的所有点,并维护好树状数组。(复杂度为O(m+nlog(n)))

6、逆序跑询问,第一个操作就加边,加边方法同流程4,第二个操作便是求出LCA,然后用树状数组扒出每个点的深度,然后加加减减得到结果并存起来。(复杂度O(qlog(n)+nlog(n))))

7、输出结果。(复杂度O(q))

 

代码(889MS):

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <iostream>
  4 #include <cstring>
  5 #include <vector>
  6 #include <cctype>
  7 using namespace std;
  8 typedef long long LL;
  9 
 10 const int MAXV = 30010;
 11 const int MAXQ = 100010;
 12 const int MAX_LOG = 16;
 13 const int INF = 0x3f3f3f3f;
 14 const int MOD = 1e9 + 7;
 15 
 16 int readint() {
 17     char c = getchar();
 18     while(!isdigit(c)) c = getchar();
 19     int res = 0;
 20     while(isdigit(c)) res = res * 10 + c - '0', c = getchar();
 21     return res;
 22 }
 23 
 24 struct Node {
 25     int to, del;
 26     Node(int to): to(to), del(0) {}
 27     bool operator < (const Node &rhs) const {
 28         if(to != rhs.to) return to < rhs.to;
 29         return del > rhs.del;
 30     }
 31 };
 32 vector<Node> adjs[MAXV];
 33 int n, m, q, T;
 34 
 35 void init() {
 36     for(int i = 1; i <= n; ++i) {
 37         adjs[i].clear();
 38     }
 39 }
 40 
 41 void add_edge(int u, int v) {
 42     adjs[u].push_back(Node(v));
 43     adjs[v].push_back(Node(u));
 44 }
 45 
 46 struct Query {
 47     int op, a, b, apos, bpos;
 48     void read() {
 49         scanf("%d%d%d", &op, &a, &b);
 50         if(op == 1) {
 51             lower_bound(adjs[a].begin(), adjs[a].end(), Node(b))->del = true;
 52             lower_bound(adjs[b].begin(), adjs[b].end(), Node(a))->del = true;
 53         }
 54     }
 55 } query[MAXQ];
 56 int ans[MAXQ], acnt;
 57 
 58 struct BIT {
 59     int tree[MAXV];
 60     void init() {
 61         memset(tree + 1, 0, n * sizeof(int));
 62     }
 63     int lowbit(int x) {
 64         return x & -x;
 65     }
 66     void modify(int x, int val) {
 67         while(x <= n) {
 68             tree[x] += val;
 69             x += lowbit(x);
 70         }
 71     }
 72     void modify(int a, int b, int val) {
 73         modify(a, val);
 74         modify(b + 1, -val);
 75     }
 76     int get_val(int x) {
 77         int res = 0;
 78         while(x) {
 79             res += tree[x];
 80             x -= lowbit(x);
 81         }
 82         return res;
 83     }
 84 } bitree;
 85 
 86 int bid[MAXV], eid[MAXV], dep[MAXV];
 87 int fa[MAX_LOG][MAXV];
 88 int dfs_clock;
 89 
 90 void dfs_id(int u, int f, int depth) {
 91     if(f > 0) adjs[u].erase(lower_bound(adjs[u].begin(), adjs[u].end(), Node(f)));
 92     fa[0][u] = f;
 93     dep[u] = depth;
 94     bid[u] = ++dfs_clock;
 95     for(Node& p : adjs[u]) if(!p.del && !bid[p.to]) {
 96         p.del = 1;
 97         dfs_id(p.to, u, depth + 1);
 98     }
 99     eid[u] = dfs_clock;
100     bitree.modify(bid[u], eid[u], 1);
101 }
102 void bit_init() {
103     memset(bid + 1, 0, n * sizeof(int));
104     bitree.init();
105     dfs_clock = 0;
106     dfs_id(1, 0, 0);
107 }
108 
109 struct LCA {
110     void init_lca() {
111         for(int k = 0; k + 1 < MAX_LOG; ++k) {
112             for(int u = 1; u <= n; ++u) {
113                 if(fa[k][u] == -1) fa[k + 1][u] = -1;
114                 else fa[k + 1][u] = fa[k][fa[k][u]];
115             }
116         }
117     }
118 
119     int ask(int u, int v) {
120         if(dep[u] < dep[v]) swap(u, v);
121         for(int k = 0; k < MAX_LOG; ++k) {
122             if((dep[u] - dep[v]) & (1 << k)) u = fa[k][u];
123         }
124         if(u == v) return u;
125         for(int k = MAX_LOG - 1; k >= 0; --k) {
126             if(fa[k][u] != fa[k][v])
127                 u = fa[k][u], v = fa[k][v];
128         }
129         return fa[0][u];
130     }
131 } lca;
132 
133 int dsu[MAXV];
134 
135 int find_set(int x) {
136     return dsu[x] == x ? x : dsu[x] = find_set(dsu[x]);
137 }
138 
139 void mergeFa(int u, int gold) {
140     u = find_set(u);
141     while(u != gold) {
142         int t = find_set(fa[0][u]);
143         dsu[u] = t;
144         bitree.modify(bid[u], eid[u], -1);
145         u = t;
146     }
147 }
148 
149 void merge(int u, int v) {
150     int l = find_set(lca.ask(u, v));
151     mergeFa(u, l);
152     mergeFa(v, l);
153 }
154 
155 void init_tree() {
156     for(int i = 1; i <= n; ++i)
157         dsu[i] = i;
158     for(int u = 1; u <= n; ++u)
159         for(Node p : adjs[u]) if(!p.del) {
160             merge(u, p.to);
161         }
162 }
163 
164 void solve() {
165     bit_init();
166     lca.init_lca();
167     init_tree();
168     for(int i = q - 1; i >= 0; --i) {
169         if(query[i].op == 1) {
170             merge(query[i].a, query[i].b);
171         } else {
172             int l = lca.ask(query[i].a, query[i].b);
173             ans[acnt++] = bitree.get_val(bid[query[i].a]) + bitree.get_val(bid[query[i].b]) - 2 * bitree.get_val(bid[l]);
174         }
175     }
176     for(int i = acnt - 1; i >= 0; --i)
177         printf("%d\n", ans[i]);
178 }
179 
180 int main() {
181     scanf("%d", &T);
182     for(int t = 1; t <= T; ++t) {
183         scanf("%d%d%d", &n, &m, &q);
184         init();
185         for(int i = 0, u, v; i < m; ++i) {
186             u = readint(), v = readint();
187             add_edge(u, v);
188         }
189         for(int i = 1; i <= n; ++i)
190             sort(adjs[i].begin(), adjs[i].end());
191 
192         acnt = 0;
193         for(int i = 0; i < q; ++i)
194             query[i].read();
195 
196         printf("Case #%d:\n", t);
197         solve();
198     }
199 }
View Code

 

posted @ 2015-09-19 17:51  Oyking  阅读(738)  评论(3编辑  收藏