牛客挑战赛51 B.NIT的图(二分查找)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客挑战赛51 B.NIT的图(二分查找)相关的知识,希望对你有一定的参考价值。
先 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∗(pi−1)/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∗(pi−1)/2−edgei
添加这 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的图(二分查找)的主要内容,如果未能解决你的问题,请参考以下文章