版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/My_stage/article/details/77160736
http://codeforces.com/contest/745/problem/C
题意:
哇,这个题好啊,说的就是,有n个节点,m条边的一个图,这个图里有k个节点是比较牛逼的节点,所以这k个节点不能连接。那么,我想知道我最多还可以再往这个图里加多少条边呢????
思路:
先用并查集做,先记录下牛逼的节点,之后再把m个边连起来。统计下每个集合内元素的个数。因为不牛逼的节点可以和牛逼的节点连接。所以我们标记所有牛逼节点的头目。即find(c[i]),之后我们对于每个是根节点的集合,看他是否牛逼,如果牛逼,我们取最大的牛逼节点的集合。反之我们记录下这个节点的孩子个数。之后我们要加上这个集合的最大边数,即 sum[i]*(sum[i]-1)/2.. 因为我们记录的所有以不牛逼节点为根的集合的数目,这些节点可以相互连接。所以把答案相加。之后我们把这些节点和那个集合数最大的节点相连接。我们得到了这个图的所有边。因为一开始有m边,所以我们要减去m条边。
#include <bits/stdc++.h>
#define maxs 202002
#define mme(i,j) memset(i,j,sizeof(i))
using namespace std;
int fa [maxs];
int sum[maxs];
int c[maxs];
int findfa(int x){
return x==fa[x]?fa[x]:fa[x] = findfa(fa[x]);
}
void joint(int x,int y){
int rx=findfa(x),ry=findfa(y);
if(rx!=ry)
{
fa[rx]=ry;
sum[ry]+=sum[rx];
}
}
bool vis[maxs];
vector<int>vec;
int main(){
int n,m,k;
while(~scanf("%d%d%d",&n,&m,&k))
{
vec.clear();
for(int i=0;i<=n;i++) {
fa[i]=i;
sum[i]=1;
}
for(int i=0;i<k;i++)
scanf("%d",&c[i]);
int x,y;
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
joint(x,y);
}
mme(vis,0);
for(int i=0;i<k;i++)
vis[findfa(c[i])]=1;
int mx=-1;
long long ans=0;
for(int i=1;i<=n;i++){
if(fa[i]==i){
if(vis[i]==1)
mx = max(mx,sum[i]);
else
vec.push_back(sum[i]);
ans+=sum[i]*(sum[i]-1)/2;
}
}
int sz = vec.size();
for(int i=0;i<sz;i++)
{
for(int j=i+1;j<sz;j++){
ans+=vec[i]*vec[j];
}
ans+=vec[i]*mx;
}
ans-=m;
printf("%lld\n",ans);
}
return 0;
}