牛客挑战赛51 B.NIT的图(二分查找)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客挑战赛51 B.NIT的图(二分查找)相关的知识,希望对你有一定的参考价值。

LINK

d f s dfs dfs求出所有连通块

设这个连通块点数为 p i p_i pi,边数为 e d g e i edge_i edgei,那么这个连通块离完全图还剩 p i ∗ ( p i − 1 ) / 2 p_i*(p_i-1)/2 pi(pi1)/2条边可以加

s u m = ∑ p i ∗ ( p i − 1 ) / 2 − e d g e i sum=\\sum p_i*(p_i-1)/2-edge_i sum=pi(pi1)/2edgei

添加这 s u m sum sum条边,连通块数量不变

否则,就需要在两个连通块之间连边了,先对连通块以 p i p_i pi为关键字排个序

然后让第一个连通块和第二个联通块连边,连通块数量减一

让前两个连通块和第三个连通块连边,连通块数量减一…

以此类推,需要的边可以算出来,定义 c o s t [ i ] cost[i] cost[i]表示连通块减小 i i i的最大边数

于是可以使用二分查找

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
#define int long long
typedef long long ll;
int n,m,q,vis[maxn],id;
vector<int>vec[maxn];
int point,edge,a[maxn];
ll cost[maxn];
void dfs(int u)
{
	vis[u] = 1; point++; 
	for(auto v:vec[u] )
	{
		edge++;
		if( vis[v] )	continue;
		dfs( v );
	}
}
bool com(int a,int b){ return a>b; }
signed main()
{
	cin >> n >> m >> q;
	for(int i=1;i<=m;i++)
	{
		int l,r; scanf("%lld%lld",&l,&r);
		vec[l].push_back( r ); vec[r].push_back( l );
	}
	ll sum = 0;
	for(int i=1;i<=n;i++)
	{
		if( vis[i] )	continue;
		point = edge = 0; dfs( i ); edge /= 2;
		a[++id] = point;
		sum += 1ll*point*(point-1)/2-edge;
	}
	sort( a+1,a+1+id,com );
	
	int pre = 0;
	for(int i=1;i<id;i++)
	{
		pre += a[i];
		cost[i] = 1ll*pre*a[i+1];
	}
	for(int i=1;i<id;i++)	cost[i] += cost[i-1];
	while( q-- )
	{
		ll x; scanf("%lld",&x);
		if( x<=sum )	printf("%lld\\n",id );
		else
		{
			x -= sum;//所有连通块都饱和了 
			int ID = lower_bound(cost+1,cost+id,x)-cost;
			if( ID<=id-1 )	printf("%lld\\n",id-ID);
			else	printf("1\\n");
		}
	}
}

以上是关于牛客挑战赛51 B.NIT的图(二分查找)的主要内容,如果未能解决你的问题,请参考以下文章

牛客题霸——二分查找(Javascript)

牛客Top200---二分查找II

基础算法(数据结构笔试复测Leecode牛客)

牛客挑战赛51 A.NIT的签到题(暴力gcd)

牛客挑战赛51 E NIT的gcd(欧拉反演,建图优化,三元环计数)

二分查找,边界思维的惊险挑战