文章目录
AT_abc350_d [ABC350D] New Friends
Link:Luogu - AT_abc350_d
题面翻译
有一个由 N N N 用户使用的 SNS,标有从 1 1 1 到 N N N 的编号。
在这个 SNS 中,两个用户可以互相成为好友。好友关系是双向的:如果用户 X 是用户 Y 的好友,那么用户 Y 也是用户 X 的好友。
目前,SNS 上有 M M M 对好友关系,其中第 i i i 对好友由用户 A i A_i Ai 和 B i B_i Bi 组成。
请确定以下操作的最大执行次数:
操作:选择三个用户 X、Y 和 Z,其中 X 和 Y 是好友,Y 和 Z 是好友,但 X 和 Z 不是好友,使 X 和 Z 成为好友。
限制因素
2 ≤ N ≤ 2 × 1 0 5 2 \leq N \leq 2 \times 10^5 2≤N≤2×105
0 ≤ M ≤ 2 × 1 0 5 0 \leq M \leq 2 \times 10^5 0≤M≤2×105
1 ≤ A i < B i ≤ N 1 \leq A_i < B_i \leq N 1≤Ai<Bi≤N
( A i , B i ) (A_i, B_i) (Ai,Bi) 成对的数据是不同的。
所有输入值均为整数。
输入格式
N
N
N
M
M
M
A
1
A_1
A1
B
1
B_1
B1
⋮
A
M
A_M
AM
B
M
B_M
BM
其中 N N N 和 M M M 分别是用户个数、好友关系数,之后的 M M M 行,每行两个整数,表示 A A A 和 B B B 是好友。
输出格式
一个整数表示最多能进行几次操作。
样例 #1
样例输入 #1
4 3
1 2
2 3
1 4
样例输出 #1
3
样例 #2
样例输入 #2
3 0
样例输出 #2
0
样例 #3
样例输入 #3
10 8
1 2
2 3
3 4
4 5
6 7
7 8
8 9
9 10
样例输出 #3
12
提示
Sample Explanation 1
次のようにして「友達の友達と新たに友達になる」という操作は 3 3 3 回行えます。 - ユーザ 1 1 1 が友達(ユーザ 2 2 2)の友達であるユーザ 3 3 3 と新たに友達になる - ユーザ 3 3 3 が友達(ユーザ 1 1 1)の友達であるユーザ 4 4 4 と新たに友達になる - ユーザ 2 2 2 が友達(ユーザ 1 1 1)の友達であるユーザ 4 4 4 と新たに友達になる 4 4 4 回以上行うことはできません。
Sample Explanation 2
もともと友達関係が存在しないとき、新たな友達関係は発生しません。
题目描述
1 1 1 から N N N の番号がついた N N N 人のユーザが利用している SNS があります。
この SNS では
2
2
2 人のユーザが互いに友達になれる機能があります。
友達関係は双方向的です。すなわち、ユーザ X がユーザ Y の友達であるならば、必ずユーザ Y はユーザ X の友達です。
現在 SNS 上には M M M 組の友達関係が存在し、 i i i 組目の友達関係はユーザ A i A_i Ai とユーザ B i B_i Bi からなります。
以下の操作を行える最大の回数を求めてください。
- 操作:3 人のユーザ X, Y, Z であって、X と Y は友達、Y と Z は友達であり、X と Z は友達でないようなものを選ぶ。X と Z を友達にする
解题思路
形式化题意:找出原图中所有连通块,每个连通块都是一个子图,求要把所有子图变成完全子图,要连多少条边。
因为一张图最大的连边个数就是变成完全图,在无重边的情况下,已经是完全图的图无法再连边。
完全图:任意两点之间都有边相连的图。在 n n n 个点的完全图中,有 1 + 2 + . . . + n − 1 = n ( n − 1 ) 2 1+2+...+n-1=\dfrac{n(n-1)}{2} 1+2+...+n−1=2n(n−1) 条边。
所以,设第
i
i
i 个子图有
a
[
i
]
a[i]
a[i] 个点,边数为
b
[
i
]
b[i]
b[i],共有
B
c
n
t
Bcnt
Bcnt 个子图(B 是 Block 的意思),那么最终答案就是:
∑
i
=
1
B
c
n
t
(
a
[
i
]
(
a
[
i
]
−
1
)
2
−
b
[
i
]
)
\ \ \ \ \sum\limits_{i=1}^{Bcnt} (\dfrac{a[i](a[i]-1)}{2} - b[i])
i=1∑Bcnt(2a[i](a[i]−1)−b[i])
= ∑ i = 1 B c n t ( a [ i ] ( a [ i ] − 1 ) 2 ) − ∑ i = 1 B c n t b [ i ] ) =\sum\limits_{i=1}^{Bcnt} (\dfrac{a[i](a[i]-1)}{2}) - \sum\limits_{i=1}^{Bcnt}b[i]) =i=1∑Bcnt(2a[i](a[i]−1))−i=1∑Bcntb[i])
= ∑ i = 1 B c n t ( a [ i ] ( a [ i ] − 1 ) 2 ) − m =\sum\limits_{i=1}^{Bcnt} (\dfrac{a[i](a[i]-1)}{2}) - m =i=1∑Bcnt(2a[i](a[i]−1))−m(因为整个大图上,所有边的个数是 m m m。也就相当于所有连通块的边数之和就是 m m m)
然后 dfs 模拟即可。注意开 long long。
(这题也可以用并查集,思路一模一样,只是实现方法的问题)
AC Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 7, maxm = 2e5 + 7;
int n, m, Ecnt;
int head[maxn], to[2 * maxm], nxt[2 * maxm]; // 注意这道题是无向边,要开2倍空间
void addEdge(int u, int v){
to[++ Ecnt] = v;
nxt[Ecnt] = head[u];
head[u] = Ecnt;
}
int Bcnt, a[maxn]; // Block—块数组的计数器,a[i]表示子图i点的个数
int vis[maxn]; // vis[i]表示点i是否被访问过
void dfs(int u){
vis[u] = 1;
a[Bcnt] ++; // 统计点
for(int i = head[u]; i; i = nxt[i]){
int v = to[i];
if(vis[v] == 0){
dfs(v);
}
}
}
void solve()
{
cin >> n >> m;
for(int i = 1, A, B; i <= m; i ++){
cin >> A >> B;
addEdge(A, B);
addEdge(B, A); // 注意这里建的是双向边
}
for(int i = 1; i <= n; i ++){
if(vis[i] == 0){
++ Bcnt;
dfs(i);
}
}
long long cnt = 0; // 统计答案
for(int i = 1; i <= Bcnt; i ++){
cnt += a[i] * (a[i]-1LL) / 2; // 这里a[i]别忘了转一下longlong
}
cout << cnt - m << '\n';
}
signed main()
{
ios :: sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
solve();
return 0;
}
End
这里是 YLCHUP,谢谢大家!