首先先把原图中的连通信息求一下,不妨设其中有\(tot\)个连通块,每个连通块的大小为\(sz_i\)
考虑第二步操作时我们需要连\(tot-1\)条边使得图连通,而每个连通块中只有\(\min(sz_i,k)\)个点可以参与连边
因此如果\(\sum_{i=1}^{tot} \min(sz_i,k)\ge 2\times (tot-1)\)的话此时的局面就合法了,否则我们需要添加一些边来连接某些连通块
稍加观察后会发现每次选\(sz_i\)最小的两个连通块合并一定是最优的,因为只要不触发\(k\)的bound的话左边的值减去右边的值一定会增加\(2\),而为了尽量保证不触发限制选小的先合并一定最优
用个堆维护下合并过程即可,总复杂度\(O(n\log n)\)
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=1e6+5;
int n,m,k,x,y,fa[N],sz[N],tot,ret,ans;
inline int getfa(CI x)
{
return fa[x]!=x?fa[x]=getfa(fa[x]):x;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (scanf("%d%d%d",&n,&m,&k),i=1;i<=n;++i) fa[i]=i;
for (i=1;i<=m;++i) scanf("%d%d",&x,&y),fa[getfa(x)]=getfa(y);
for (i=1;i<=n;++i) ++sz[getfa(i)];
for (i=1;i<=n;++i) if (getfa(i)==i) ++tot,ret+=min(sz[i],k);
priority_queue <pi,vector <pi>,greater <pi>> hp;
for (i=1;i<=n;++i) if (getfa(i)==i) hp.push(pi(sz[i],i));
while (ret<2*(tot-1))
{
int x=hp.top().se; hp.pop();
int y=hp.top().se; hp.pop();
ret-=min(sz[x],k)+min(sz[y],k);
sz[x]+=sz[y]; fa[getfa(y)]=getfa(x);
ret+=min(sz[x],k); --tot; ++ans;
hp.push(pi(sz[x],x));
}
return printf("%d",ans),0;
}
标签:typedef,FreeDiv,int,连通,long,CF73D,include,define
From: https://www.cnblogs.com/cjjsb/p/17773251.html