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] - 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, f;
 26     Node(int to, int f = 0): to(to), del(0), f(f) {}
 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, adjs[v].size()));
 43     adjs[v].push_back(Node(u, adjs[u].size() - 1));
 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             auto it = lower_bound(adjs[a].begin(), adjs[a].end(), Node(b));
 52             it->del = adjs[b][it->f].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];
 87 int dfs_clock;
 88
 89 void dfs_id(int u) {
 90     bid[u] = ++dfs_clock;
 91     for(Node& p : adjs[u]) if(!p.del && !bid[p.to]) {
 92         p.del = adjs[p.to][p.f].del = 2;
 93         dfs_id(p.to);
 94     }
 95     eid[u] = dfs_clock;
 96     bitree.modify(bid[u], eid[u], 1);
 97 }
 98 void bit_init() {
 99     memset(bid + 1, 0, n * sizeof(int));
100     bitree.init();
101     dfs_id(1);
102 }
103
104 struct LCA {
105     int fa[MAX_LOG][MAXV];
106     int dep[MAXV];
107
108     void dfs_lca(int u, int f, int depth) {
109         dep[u] = depth;
110         fa[0][u] = f;
111         for(Node p : adjs[u]) if(p.del == 2 && p.to != f)
112             dfs_lca(p.to, u, depth + 1);
113     }
114
115     void init_lca() {
116         dfs_lca(1, -1, 0);
117         for(int k = 0; k + 1 < MAX_LOG; ++k) {
118             for(int u = 1; u <= n; ++u) {
119                 if(fa[k][u] == -1) fa[k + 1][u] = -1;
120                 else fa[k + 1][u] = fa[k][fa[k][u]];
121             }
122         }
123     }
124
125     int ask(int u, int v) {
126         if(dep[u] < dep[v]) swap(u, v);
127         for(int k = 0; k < MAX_LOG; ++k) {
128             if((dep[u] - dep[v]) & (1 << k)) u = fa[k][u];
129         }
130         if(u == v) return u;
131         for(int k = MAX_LOG - 1; k >= 0; --k) {
132             if(fa[k][u] != fa[k][v])
133                 u = fa[k][u], v = fa[k][v];
134         }
135         return fa[0][u];
136     }
137 } lca;
138
139 int dsu[MAXV];
140
141 int find_set(int x) {
142     return dsu[x] == x ? x : dsu[x] = find_set(dsu[x]);
143 }
144
145 void mergeFa(int u, int gold) {
146     u = find_set(u);
147     while(u != gold) {
148         int t = find_set(lca.fa[0][u]);
149         dsu[u] = t;
150         bitree.modify(bid[u], eid[u], -1);
151         u = t;
152     }
153 }
154
155 void merge(int u, int v) {
156     int l = find_set(lca.ask(u, v));
157     mergeFa(u, l);
158     mergeFa(v, l);
159 }
160
161 void init_tree() {
162     for(int i = 1; i <= n; ++i)
163         dsu[i] = i;
164     for(int u = 1; u <= n; ++u)
165         for(Node p : adjs[u]) if(!p.del) {
166             merge(u, p.to);
167             p.del = adjs[p.to][p.f].del = 2;
168         }
169 }
170
171 void solve() {
172     bit_init();
173     lca.init_lca();
174     init_tree();
175     for(int i = q - 1; i >= 0; --i) {
176         if(query[i].op == 1) {
177             merge(query[i].a, query[i].b);
178         } else {
179             int l = lca.ask(query[i].a, query[i].b);
180             ans[acnt++] = bitree.get_val(bid[query[i].a]) + bitree.get_val(bid[query[i].b]) - 2 * bitree.get_val(bid[l]);
181         }
182     }
183     for(int i = acnt - 1; i >= 0; --i)
184         printf("%d\n", ans[i]);
185 }
186
187 int main() {
188     scanf("%d", &T);
189     for(int t = 1; t <= T; ++t) {
190         scanf("%d%d%d", &n, &m, &q);
191         init();
192         for(int i = 0, u, v; i < m; ++i) {
193             u = readint(), v = readint();
194             add_edge(u, v);
195         }
196         for(int i = 1; i <= n; ++i)
197             sort(adjs[i].begin(), adjs[i].end());
198
199         acnt = 0;
200         for(int i = 0; i < q; ++i)
201             query[i].read();
202
203         printf("Case #%d:\n", t);
204         solve();
205     }
206 }

时间: 2024-10-10 20:50:24

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

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

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

Hdu 5452 Minimum Cut (2015 ACM/ICPC Asia Regional Shenyang Online) dfs + LCA

题目链接: Hdu 5452 Minimum Cut 题目描述: 有一棵生成树,有n个点,给出m-n+1条边,截断一条生成树上的边后,再截断至少多少条边才能使图不连通, 问截断总边数? 解题思路: 因为只能在生成树上截断一条边(u, v),所以只需要统计以v为根节点的子生成树里的节点与子生成树外的节点的边数就可以了.对于新加入的边(u', v')来说,只影响以LCA(u, v)为根节点的子树里面的节点.统计所有答案,扫一遍输出最小即可.(比赛的时候只统计叶子节点,给水过去了........233

(字符串处理)Fang Fang -- hdu -- 5455 (2015 ACM/ICPC Asia Regional Shenyang Online)

链接: http://acm.hdu.edu.cn/showproblem.php?pid=5455 Fang Fang Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 233    Accepted Submission(s): 110 Problem Description Fang Fang says she wants to be

Hdu 5451 Best Solver (2015 ACM/ICPC Asia Regional Shenyang Online) 暴力找循环节 + 递推

题目链接: Hdu  5451  Best Solver 题目描述: 对于,给出x和mod,求y向下取整后取余mod的值为多少? 解题思路: x的取值为[1, 232],看到这个指数,我的心情是异常崩溃的.(吐血......) 可是仔细观察,它指数大,可是mod小啊,它吓人,可是可以暴力搞啊!! 这个题目一个难点就是要向下取整求余,详解见传送门,本题是向下取整,也就是向上取整加一. 还有就是指数太大,要找到循环节,其实由于mod小,循环节并没有太大,暴力跑就ok啦!  此刻内心是崩溃的 1 #i

Hdu 5459 Jesus Is Here (2015 ACM/ICPC Asia Regional Shenyang Online) 递推

题目链接: Hdu 5459 Jesus Is Here 题目描述: s1 = 'c', s2 = 'ff', s3 = s1 + s2; 问sn里面所有的字符c的距离是多少? 解题思路: 直觉告诉我们,sn肯定由sn-1与sn-2推导出来的.然后呢,我们可以看出 n%2==1 的时候 sn-1 与 sn-2 由 ffff 衔接起来的,n%2==0 的时候,sn-1 与 sn-2由 ff 衔接起来的.告诉队友后,队友就把这个当成重要依据推啊,推啊!!到最后感觉丢队友自己看药丸,放弃02回来和队友

(并查集)Travel -- hdu -- 5441(2015 ACM/ICPC Asia Regional Changchun Online )

http://acm.hdu.edu.cn/showproblem.php?pid=5441 Travel Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 2061    Accepted Submission(s): 711 Problem Description Jack likes to travel around the wo

【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,这个数是非常小的. 所以我们完全可以暴力修改,只需要知道每次的修改区间中有多少真

2015 ACM/ICPC Asia Regional Changchun Online HDU 5444 Elven Postman【二叉排序树的建树和遍历查找】

Elven Postman Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 591    Accepted Submission(s): 329 Problem Description Elves are very peculiar creatures. As we all know, they can live for a very

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