题目链接:
题目描述:
给出一个还有环和重边的图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 ", 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: ", i); 233 solve (); 234 235 } 236 return 0; 237 }