最大数量

最大数量

一个无向图有 $n$ 个点,编号 $1 \sim n$。

这些点之间没有任何边。

给定 $d$ 个需求,编号 $1 \sim d$。

其中,第 $i$ 个需求是让点 $x_i$ 和点 $y_i$ 连通。

需求可能存在重复。

在本题中,你需要依次解决 $d$ 个问题,编号 $1 \sim d$。

其中,第 $i$ 个问题是,请你在图中添加恰好 $i$ 条无向边(不能添加重边和自环),使得:

  1. 前 $i$ 个需求都得到满足。
  2. 所有点中度最大的点的度尽可能大。

对于每个问题,你不需要输出具体方案,你只需要输出度的最大可能值。

注意:

  1. 如果点 $a$ 和点 $b$ 之间存在路径,则称点 $a$ 和点 $b$ 连通。
  2. 图中与点 $a$ 关联的边数,称为点 $a$ 的度。(如果一个点是一条边的端点,则称这个点和这条边关联)
  3. $d$ 个问题之间是相互独立的,每个问题的答案都必须独立计算。

输入格式

第一行包含两个整数 $n,d$。

接下来 $d$ 行,其中第 i 行包含两个整数 $x_i,y_i$,表示第 i 个需求是让点 $x_i$ 和点 $y_i$ 连通。

输出格式

共 $d$ 行,其中第 $i$ 行输出第 $i$ 个问题中,度的最大可能值。

数据范围

前三个测试点满足,$2 \leq n \leq 10$。

所有测试点满足,$2 \leq n \leq 1000$,$1 \leq d \leq n−1$,$1 \leq x_i,y_i \leq n$,$x_i \ne y_i$。

输入样例1:

7 6
1 2
3 4
2 4
7 6
6 5
1 7

输出样例1:

1
1
3
3
3
6

输入样例2:

10 8
1 2
2 3
3 4
1 4
6 7
8 9
8 10
1 4

输出样例2:

1
2
3
4
5
5
6
8

 

解题思路

  这题读了半天都不知道在说什么,对于第$i$个问题恰好添加$i$条边,一直不知道是指在第$i$个问题加$i$条边还是之前的问题加起来恰好$i$条边。后面看了题解才知道每个问题都是独立的,每次考虑前$i$个问题就可以了,对于第$i$个问题,可以重新构图,添加$i$条边来满足前$i$个条件。

  对于每个询问,要满足$x_i$和$y_i$连通,因此可以用并查集来维护。因此对于前$i$个问题,在添加完$i$条边后就会形成若干个连通块,假设有$m$个连通块,第$i$个连通块的大小为$\text{cnt}_i$。由于每个连通块相互独立,因此在考虑度数的时候分别看每个连通的最大度数。对于第$i$个连通块,我们可以构造一个菊花图,使得某个点的最大度数为$\text{cnt}_i - 1$。

  对于前$i$个询问,每个询问最多添加一条边就可以满足需求,因此添加$i$条边是一定可以满足前$i$个需求,有解。但实际上我们不一定要用$i$条边。比如如果$x_i$与$y_i$本身就已经连通了,如果我们再对$x_i$与$y_i$连一条边,实际上就是在该连通块内连一条边,并不能增加最大度数(某个连通块的最大度数都是$\text{cnt}_i - 1$,即连通块内点的个数减$1$)。因此对于多出来的边,我们可以用来连通某两个连通块,这样两个连通块就合并成了一个,新的连通块的某个点的最大度数就是$\text{cnt}_i + \text{cnt}_j - 1$,度数就变大了。

  因此对于前$i$个询问,看一下有多少条多余的边,假设为$s$,那么我们可以用这$s$条边来合并$s + 1$个连通块。为了使得得到的度数最大,可以按照连通块从大到小的顺序来合并。

  AC代码如下,时间复杂度为$O(n^2 \log {n})$:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1010;
 5 
 6 int fa[N], cnt[N];
 7 
 8 int find(int x) {
 9     return fa[x] == x ? fa[x] : find(fa[x]);
10 }
11 
12 int main() {
13     int n, m;
14     scanf("%d %d", &n, &m);
15     for (int i = 1; i <= n; i++) {
16         fa[i] = i;
17         cnt[i] = 1;
18     }
19     int s = 0;
20     vector<int> tmp;
21     for (int i = 1; i <= m; i++) {
22         int x, y;
23         scanf("%d %d", &x, &y);
24         x = find(x), y = find(y);
25         if (x != y) {
26             cnt[x] += cnt[y];
27             fa[y] = x;
28         }
29         else {  // x和y已经在同一个连通块,不需要再连边了
30             s++;
31         }
32         tmp.clear();
33         for (int i = 1; i <= n; i++) {
34             if (fa[i] == i) tmp.push_back(cnt[i]);
35         }
36         sort(tmp.begin(), tmp.end(), greater<int>());
37         int ret = 0;
38         for (int i = 0; i < tmp.size() && i < s + 1; i++) { // 选出最大的s+1个连通块进行合并
39             ret += tmp[i];
40         }
41         printf("%d\n", ret - 1);    // 最大度数就是构造菊花图,连通块的大小减1
42     }
43     
44     return 0;
45 }

 

日记

  这刚好是我的第$300$篇博客,也是我来博客园写博客的第$2$年,回想起当时什么都不会的我,再到现在已经掌握了大部分算法,还蛮多感慨的。我很怀念写博客的这段时间,因为我还蛮喜欢分享一些知识的见解和个人的想法,不过比较糟糕的是并没有很多人访问我的博客,感觉帮助到的人会比较少。不过这也或许说明了我的文字表达能力并不那么的优秀,博文的质量也没那么高,因此还有很多需要学习的地方。

  在这两年我学到了很多的数学和算法知识,也更加坚定了对数学和算法的喜爱。不过最近我的状态还蛮糟糕的,一方面不知道有没有机会保研,即使我已经尽我最大的努力,但还是一个概率的问题。另一方面是我找不到适合我的专业。虽说我是计算机专业,但实际上我更想读数学的专业。所以我找了$\text{tcs}$这个方向的专业,但不幸的是国内在这方面的资源十分匮乏,基本都集中在了国内最顶尖的高校,很明显我这种普通人是不可能达到这种高度的。所以我现在的情况还挺矛盾的。未来会怎么样真的很模糊,有很多不确定的因素,这也是我现在总是担心的地方。

  但不管怎么样,只要我对数学和算法的热情不减,就一定会继续写博客与网友分享我的知识和见解。

  最后放张《魔法少女小圆》的图吧,我很喜欢的一部动漫。话说我写了这么多的博客都没放过二次元的图片。

 

参考资料

  AcWing 4866. 最大数量(AcWing杯 - 周赛):https://www.acwing.com/video/4638/

posted @ 2023-03-01 10:46  onlyblues  阅读(11)  评论(0编辑  收藏  举报
Web Analytics