题意

有 $n$ 个点,初始没有连边,要求支持两个动态操作:

1. 加一条边(保证之前两点不连通)

2. 查询过一条边的简单路径数量(就是两边连通块的大小的乘积)

$n,Q\le 100000$

题解

由第一个操作保证之前两点不连通的性质可知,$n$ 个点最多被连成一棵 $n-1$ 条边的生成树。

离线做法:树剖+树状数组/线段树

我们可以离线确定出最终森林的形态(我们给森林中的每棵树随便定义个根)。

然后重新模拟操作时,只需要在最终的森林上做一些链操作(统计答案用)。

由于只有加边操作,所以模拟加边时,用并查集维护连通树及当前连通树的根

当前连通树的根就是在加一些边后,一个点所在连通树的深度最小的点,这个深度根据最终这棵树的形态而定,越靠近根深度越小。

同时我们还要维护当前以每个点为根的子树的大小。

 

这样,插入一条边时,假设在最终的树形态中 $u$ 是 $v$ 的父亲(这个是已知的,因为我们随便定了一个根),那我们在合并两个连通树时,把 $v$ 所在并查集连向 $u$ 所在并查集。

由于并查集只维护连通树的根,我们要把从 $u$ 到其所在连通树的当前根的所有点都更新子树大小。

画个图

【BJOI2014/bzoj4530】大融合

现在有一条边要连通如图两棵子树

【BJOI2014/bzoj4530】大融合

那么所有橙色点(即u到根的路径)都要更新子树大小

【BJOI2014/bzoj4530】大融合

根据最终树的形态来确定 $u$、$v$ 的合并方向,就是为了在维护连通性的同时维护连通树的根。之前说过,连通树的根是 在树的最终形态中深度最小的点,如果 $u$ 是 $v$ 的父亲,那 $u$ 的深度当然比 $v$ 小,应该让 $v$ 的并查集连向 $u$ 的。

 

这样,我们实际上就是一边加边、一边维护每个点的子树大小。

加边不用实际加,只要连并查集就行了,因为在加边时,只有从其一端点 到它所在连通树的根 上的所有点要修改,路径的两端点都已知,直接树链剖分就行了。

查询……那两个点的子树 $size$ 都已知了,而且一个点还是另一个点的父亲,(设 $x$ 是 $y$ 的父亲,$rt$ 是 $x,y$ 两点所在连通树的根)答案就是 $size_y\times (size_{rt}-size_y)$。

$size_y$ 就是 $y$ 这边的点数,$size_{rt}-size_y$ 就是 $x$ 这边的点数。

画个图就是

【BJOI2014/bzoj4530】大融合

 

时间复杂度 $O(n + q\times log^2(n))$。

注:我没写这种方法,这是 $luogu$ 大佬的代码,其中的 $iota(f + 1, f + n + 1, 1);$ 就是 $for(int\space i=1;i<=n;++i)\space f[i]=i;$,即从 $1$ 开始以 $1$ 的斜率递增并依次赋值。

  1 #include <cstdio>
  2 
  3 #include <algorithm>
  4 #include <numeric>
  5 
  6 using namespace std;
  7 
  8 struct edge {
  9     int v;
 10     edge* next;
 11 };
 12 
 13 const int N = 100010;
 14 
 15 int n;
 16 char opt[N];
 17 int eu[N], ev[N], p[N], f[N], son[N], sum[N], fw[N], ord[N];
 18 int top[N];
 19 edge* g[N];
 20 
 21 int find(int x);
 22 void add_edge(int u, int v);
 23 void dfs1(int u);
 24 void dfs2(int u);
 25 void change(int k, int x);
 26 void tchange(int u, int x);
 27 int query(int k);
 28 int low_bit(int k);
 29 
 30 int main() {
 31     int q;
 32     scanf("%d%d", &n, &q);
 33     for (int i = 1; i <= q; ++i) {
 34         scanf(" %c%d%d", &opt[i], &eu[i], &ev[i]);
 35         if (opt[i] == 'A') {
 36             add_edge(eu[i], ev[i]);
 37             add_edge(ev[i], eu[i]);
 38         }
 39     }
 40     for (int u = 1; u <= n; ++u)
 41         if (!p[u]) {
 42             p[u] = -1;
 43             dfs1(u);
 44         }
 45     for (int u = 1; u <= n; ++u)
 46         if (p[u] == -1) {
 47             top[u] = u;
 48             dfs2(u);
 49         }
 50     iota(f + 1, f + n + 1, 1);
 51     for (int i = 1; i <= n; ++i) {
 52         tchange(i, 1);
 53         tchange(p[i], -1);
 54     }
 55     for (int i = 1; i <= q; ++i) {
 56         int u = eu[i], v = ev[i];
 57         if (p[u] == v)
 58             swap(u, v);
 59         int sv = query(ord[v]);
 60         if (opt[i] == 'A') {
 61             f[v] = find(u);
 62             tchange(u, sv);
 63             tchange(p[f[u]], -sv);
 64         } else {
 65             int s = query(ord[find(u)]);
 66             printf("%d\n", (s - sv) * sv);
 67         }
 68     }
 69     return 0;
 70 }
 71 
 72 void tchange(int u, int x) {
 73     while (u != -1) {
 74         change(ord[top[u]], x);
 75         change(ord[u] + 1, -x);
 76         u = p[top[u]];
 77     }
 78 }
 79 
 80 void dfs2(int u) {
 81     static int t;
 82     ord[u] = ++t;
 83     if (!son[u])
 84         return;
 85     top[son[u]] = top[u];
 86     dfs2(son[u]);
 87     for (edge* q = g[u]; q; q = q->next)
 88         if (p[q->v] == u && q->v != son[u]) {
 89             top[q->v] = q->v;
 90             dfs2(q->v);
 91         }
 92 }
 93 
 94 int low_bit(int k) {
 95     return k & -k;
 96 }
 97 
 98 void change(int k, int x) {
 99     for (; k <= n; k += low_bit(k))
100         fw[k] += x;
101 }
102 
103 int query(int k) {
104     int ret = 0;
105     for (; k; k -= low_bit(k))
106         ret += fw[k];
107     return ret;
108 }
109 
110 void dfs1(int u) {
111     sum[u] = 1;
112     for (edge* q = g[u]; q; q = q->next)
113         if (!p[q->v]) {
114             p[q->v] = u;
115             dfs1(q->v);
116             sum[u] += sum[q->v];
117             if (sum[son[u]] < sum[q->v])
118                 son[u] = q->v;
119         }
120 }
121 
122 int find(int x) {
123     return f[x] == x ? x : (f[x] = find(f[x]));
124 }
125 
126 void add_edge(int u, int v) {
127     static edge pool[N * 2];
128     static edge* p = pool;
129     p->v = v;
130     p->next = g[u];
131     g[u] = p;
132     ++p;
133 }
View Code

相关文章:

  • 2021-09-17
  • 2022-12-23
  • 2022-12-23
  • 2022-01-22
  • 2022-01-23
  • 2021-06-06
  • 2021-07-29
  • 2021-09-10
猜你喜欢
  • 2021-05-27
  • 2022-02-05
  • 2021-11-19
  • 2021-07-31
  • 2021-06-28
  • 2021-08-24
相关资源
相似解决方案