Social Network原题
题面翻译
给定 n n n 个点, d d d 条规定,每条规定有两个参数 x i , y i x_i,y_i xi,yi,表示 x i x_i xi 和 y i y_i yi 必须连通。
∀ i ∈ [ 1 , d ] \forall i\in[1,d] ∀i∈[1,d],求在满足 [ 1 , i ] [1,i] [1,i] 的规定的前提下恰好连 i i i 条边的无向图中度数最大的点的度数(就是联通块的边数)。
2 ≤ n ≤ 1 0 3 , 1 ≤ d ≤ n − 1 , 1 ≤ x i , y i ≤ n , x i ≠ y i 2\le n\le 10^3,1\le d\le n-1,1\le x_i,y_i\le n,x_i\not=y_i 2≤n≤103,1≤d≤n−1,1≤xi,yi≤n,xi=yi。
样例 #1
样例输入 #1
7 6
1 2
3 4
2 4
7 6
6 5
1 7
样例输出 #1
1
1
3
3
3
6
样例 #2
样例输入 #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
解题思路
- 思路:这题要求的是最大的度,即最大的连通块里面的边数,很显然,这是一道并查集问题,我们可以这样考虑,题目给定了 d d d条规定,每条规定 x x x, y y y两点必联通,那么如果 x x x和 y y y不具有相同的祖宗节点,我们就将 x x x和y进行merge操作(即让它们的祖宗结点相同),如果 x x x和 y y y的祖宗结点已经相同,那么此时 x x x和 y y y之间的边就是一条闲边(因为 x x x和 y y y之间已经联通),闲边可以用来连接其他连通块,用 r e s res res来记录闲边的数量。然后我们遍历完所有的连通块,找出前 r e s res res+1 大的连通块相连( r e s res res条边可以连接 r e s res res+1个连通块)然后统计边数,输出答案。
- 注意:连通块的大小要减1,因为度=大小-1.
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N=1e3+10;
int fa[N],size1[N],res=0;//fa[i]记录的是i的父节点,size[i]记录i这个点连通块中
//的大小
int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);//迭代找祖宗节点
return fa[x];
}
void merge(int x,int y){//将两个集合合并
x=find(x),y=find(y);
if(x==y) res++;//当两者的祖宗结点相同时,就可以多连出去一条边
else{
fa[x]=y;
size1[y]+=size1[x];//将两个数祖宗结点变为一致,并合并联通块大小
}
}
int main(){
int n,d;
cin>>n>>d;
for(int i=1;i<=n;i++){
fa[i]=i;//初始每个数的父节点都是i
size1[i]=1;//初始每个数自己构成一个连通块
}
for(int i=1;i<=d;i++){
vector<int> alls;
int ans=0;//记录最大连通块的度
int x,y;
cin>>x>>y;
merge(x,y);
for(int j=1;j<=n;j++){
if(j==find(j)){
alls.push_back(size1[j]);//如果j是祖宗结点,就将j的连通块大小加入
//vector
}
}
sort(alls.begin(),alls.end(),greater<int>());//将所有的连通块按从大到
//小的顺序排序
for(int j=0;j<alls.size() && j<=res;j++){
ans+=alls[j];//取前res+1大的连通块
}
ans--;//连通块的度等于大小-1
cout<<ans<<endl;
}
return 0;
}