CF73D FreeDiv

首先先把原图中的连通信息求一下,不妨设其中有\(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;
}
posted @ 2023-10-18 20:15  空気力学の詩  阅读(8)  评论(0编辑  收藏  举报