CF73D FreeDiv 题解

题目传送门 洛谷题目传送门

思路

首先,需要明白,如果在操作一后,有 x x x 个联通块,则整张图还需要 x − 1 x-1 x1 条无向边才能联通。

而,设第 i i i 个联通块的大小是 s i z i siz_i sizi,由于每个点只能连一条边,则最多能向外连到 s i z i siz_i sizi 个点,等价于可以向外连 s i z i siz_i sizi 个联通块(即每个点都在不同的联通块)。

对于操作 2 2 2,一个联通块就只能向外连 k k k 条边,所以就只能联通 min ⁡ ( s i z i , k ) \min(siz_i,k) min(sizi,k) 个块。

所以,操作二在满足 ∑ i = 1 x min ⁡ ( s i z i , k ) ≥ 2 × ( x − 1 ) \displaystyle\sum_{i=1}^{x} \min(siz_i,k)\ge 2\times(x-1) i=1xmin(sizi,k)2×(x1) 才能使图联通,注意连接两个联通块必须两个都有可以向外连的边时才行,所以右边要乘 2 2 2

那么就可以用贪心的思想从小到大合并联通块。证明如下:

如果两个联通块 x , y x,y x,y,不妨设 s i z x < s i z y siz_x<siz_y sizx<sizy,满足 s i z x ≤ k , s i z y ≤ k , s i z x + s i z y > k siz_x\le k,siz_y\le k,siz_x+siz_y>k sizxk,sizyk,sizx+sizy>k,且存在另一个联通块 z z z 使得 s i z x + s i z z ≤ k siz_x+siz_z\le k sizx+sizzk,易得 s i z z < s i z y siz_z<siz_y sizz<sizy。这样,第一种方法的贡献是 k + s i z z k+siz_z k+sizz,而第二种就是 s i z x + s i z y + s i z z siz_x+siz_y+siz_z sizx+sizy+sizz,因为 s i z x + s i z y > k siz_x+siz_y>k sizx+sizy>k,所以第二种一定比第一种优。

综上,先选小的比先选大的更优。

这个东西用堆维护就行了,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int n,m,k,f[N],siz[N],ans,now;
priority_queue<int> q;
int find(int x)
{
	if(f[x]==x) return x;
	return f[x] = find(f[x]);
}
void add(int x,int y)//维护联通块
{
	int fx = find(x),fy = find(y);
	if(fx==fy) return;
	siz[fx]+=siz[fy],siz[fy] = 0,f[fy] = fx;
}
signed main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>m>>k;
	for(int i = 1;i<=n;i++) f[i] = i,siz[i] = 1;
	for(int i = 1,x,y;i<=m;i++)
		cin>>x>>y,add(x,y);
	for(int i = 1;i<=n;i++)
		if(f[i]==i)
			q.push(-siz[i])/*大根堆维护最小值转负数*/,now+=min(siz[i],k);
	while(1)
	{
		if(now>=2*q.size()-2)//满足条件
			return cout<<ans,0;
		ans++;
		int u = -q.top();q.pop();
		int v = -q.top();q.pop();
		now+=min(k,u+v)-min(k,u)-min(k,v);
		q.push(-u-v);
	}
	return 0;
}
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值