题解:[ABC350D] New Friends

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 2N2×105

0 ≤ M ≤ 2 × 1 0 5 0 \leq M \leq 2 \times 10^5 0M2×105

1 ≤ A i < B i ≤ N 1 \leq A_i < B_i \leq N 1Ai<BiN

( 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+...+n1=2n(n1) 条边。

所以,设第 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=1Bcnt(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=1Bcnt(2a[i](a[i]1))i=1Bcntb[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=1Bcnt(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,谢谢大家!

广告:文章同步到本人洛谷博客;个人洛谷账号:ylch

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值