【並查集】2020CCPC秦皇島-F. Friendly Group
【並查集】2020CCPC秦皇島-F
題目連結(https://codeforces.com/gym/102769/problem/F)
F. Friendly Group
time limit per test 2.0 s
memory limit per test 512 megabytes
input standard input
output standard output
Professor Alex will organize students to attend an academic conference.
Alex has \(n\) excellent students, and he decides to select some of them (possibly none) to attend the conference. They form a group. Some pairs of them are friends.
The friendly value of the group is initially \(0\). For each couple of friends \((x,y)\)
Alex wants to make the group more friendly. Please output the maximum friendly value of the group.
Input
The first line of the input gives the number of test cases, \(T (1≤T≤10^4)\). \(T\) test cases follow.
For each test case, the first line contains two integers \(n (1≤n≤3×10^5)\) and \(m (1≤m≤10^6)\), where \(n\) is the number of students and mm is the number of couples of friends.
Each of the following mm lines contains two integers \(xi,yi (1≤xi,yi≤n,xi≠yi)\), representing student$ xi$ and student \(yi\)are friends. It guaranteed that unordered pairs$ (xi,yi)$ are distinct.
The sum of \(n\) in all test cases doesn't exceed \(10^6\), and the sum of \(m\) in all test cases doesn't exceed \(2×10^6\).
Output
For each test case, output one line containing "Case #x: y", where \(x\) is the test case number (starting from \(1\)), and \(y\) is the maximum friendly value of the group.
Example
input
2
4 5
1 2
1 3
1 4
2 3
3 4
2 1
1 2
output
Case #1: 1
Case #2: 0
題意
給定\(n\)個學生,其中有\(m\)對好朋友\((x,y)\),老師要挑選一些學生去比賽,設定這群學生友誼值初始為0,
如果一對好朋友都被挑選到,群體友誼值\(+1\),如果一對好朋友當中只有其中一人被選中,群體友誼值\(-1\),如果有\(k\)個學生參加了比賽,群友誼值\(-k\),問挑選出學生群最大友誼值是多少。
思路
根據樣例畫圖發現,\(答案數=通路數-人數\)
繼續加邊發現,單條的邊對通路貢獻為1,對人數t貢獻也為1,所以單成鏈的邊對答案沒有貢獻,只有單獨的環路貢獻為正
繼續考慮,環套環,也是沒有影響的
求集合最常用的演算法為並查集,故以並查集模板
最後,差點以為是求友誼值最大的群,隊友最後提出了群體不止一個(不好好讀題的鍋),所以把每個環路的正貢獻相加即可
AC程式碼
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define int long long
#define ull unsigned long long
#define PII pair<int,int>
using namespace std;
typedef long long LL;
const int N = 3e5 + 100;
const int mod = 1e9 + 7;
const double pi = acos(-1.0);
int t, n, m;
int root[N];
int road[N];//同一集合通路數
int cnt[N];//同一集合人數
int ans;
void init() {//初始化
for (int i = 0; i <= n; i++) {
root[i] = i;//初始以自己為根
road[i] = 0;//初始沒通路
cnt[i] = 1;//初始只有自己一個人
}
return;
}
int find(int x) { return x == root[x] ? x : root[x] = find(root[x]); }//路徑壓縮
void merge(int x, int y) {
int dx = find(x);
int dy = find(y);
if (dx != dy) {//不同根
cnt[dx] += cnt[dy];//集合人數疊加
road[dx] += road[dy];//集合通路數疊加
//並根在增加操作之後
root[dy] = dx;
road[dx] ++;//並根時增加的一條通路
}
else {//同根,只有通路數增加
road[dx] ++;
}
return;
}
void solve() {
init();
int x, y;
while (m--) {//加邊操作
cin >> x >> y;
if (y < x) swap(x, y);//這裡我預設前面比後面的序號大,不知道對merge有沒有影響
merge(x, y);
}
ans = 0;
for (int i = 1; i <= n; i++) {
if (root[i] == i) {
int t = road[i] - cnt[i];
if (t >= 0) ans += t;//如果貢獻為正就相加
}
}
return;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> t;
for (int i = 1; i <= t; i++) {
cin >> n >> m;
solve();
cout << "Case #" << i << ": " << ans << endl;//輸出
}
return 0;
}
/*
i raised a cute kitty in my code,
my friend who pass by can touch softly on her head:)
/l、
Meow~(゚、 。7
|、 ~ヽ
じしf_,)ノ
*/