题意:无向图 修改加查询
修改:在无向图中加边( 保证简单图:无重边和自环 )
查询:加x条边(保证加完还是简单图),最多和最少的连通分量
思路:
首先思路很好想,连通块尽量少就是连一条边减少一个连通分量。连通块尽量多就是在原有的连通分量中进行加边。
加满了就需要在两个连通块之间进行连边,我们当然希望在两个大的连通块之间进行连边。所以涉及到一个按连通块大小进
行排序的问题。易知前n个连通块所能容纳的边数与 “和”和“ 平方和 ” 有关。这是一个前缀和有关的东西,确定需要连通前 几 个连通块的方法大致可以想到是二分,于是不难想在用权值线段树上进行二分。(因为需要二分所以线段树要优于树状数组)。
维护的东西都很简单,直接上代码。
我又被 int 坑了,以后还是用 longlong 吧,T了再说。
#include <iostream>
#define lc l,mid,x << 1LL
#define rc mid+1,r,x << 1LL | 1LL
using namespace std;
typedef long long LL;
const int maxn = 100000 + 100;
LL num1[5*maxn],num2[5*maxn],num0[5*maxn];
void pushup( LL x ){
num1[x] = num1[x<<1] + num1[x<<1|1];
num2[x] = num2[x<<1] + num2[x<<1|1];
num0[x] = num0[x<<1] + num0[x<<1|1];
}
LL n;
void build( LL l,LL r,LL x){
if( l == r ){
if( l == 1 ){
num1[x] = n;
num2[x] = n;
num0[x] = n;
}
else{
num1[x] = 0;
num2[x] =0;
num0[x] = 0;
}
return;
}
LL mid = l + r >> 1LL;
build( lc );
build( rc );
pushup( x );
}
void update( LL L,LL v,LL l,LL r,LL x ){
if( l == r ){
num1[x] += v*l;
num2[x] += v * l * l;
num0[x] += v;
return;
}
LL mid = l + r >> 1LL;
if( L <= mid )update( L,v,lc );
else update( L,v,rc );
pushup(x );
}
LL ask( LL sum,LL le,LL l,LL r,LL x ){
if( l == r ){
LL ll = -1,rr = num0[x];
while( ll != rr-1 ){
LL mid = ll + rr >> 1LL;
if( (mid * l * mid * l - mid * l*l) / 2 + mid * l * sum >= le ){
rr = mid;
}else{
ll = mid;
}
}
return rr ;
}
LL sum0 = num0[x<<1LL |1LL];
LL sum1 = num1[x<<1LL |1LL];
LL sum2 = num2[x<<1LL |1LL];
LL re = (sum1 * sum1 - sum2) / 2 + sum1 * sum;
LL mid= l + r >> 1LL;
if( re >= le ){
return ask( sum,le,rc );
}else{
return sum0 + ask( sum +sum1,le-re,lc );
}
}
LL cnt[maxn],fa[maxn];
void init(LL n){
for( LL i = 0;i <= n;i++ ){
fa[i] = i;
cnt[i] = 1;
}
}
LL get( LL x ){
if( x == fa[x] ) return x;
return fa[x] = get( fa[x] );
}
int main()
{
LL T,q,f,x,y,remain;
LL contain;
scanf("%lld",&T);
while( T-- ){
scanf("%lld%lld",&n,&q);
remain = n;
init(n);
build( 1,n,1 );
contain = 0;
for( LL i = 1;i <= q;i++ ){
scanf("%lld",&f);
if( f == 1 ){
scanf("%lld%lld",&x,&y);
LL fx = get(x);
LL fy = get(y);
if( fx == fy ){
contain--;
}else{
fa[fx] = fy;
remain--;
contain += (LL)cnt[fx] * cnt[fy] - 1;
update(cnt[fx],-1,1,n,1 );
update( cnt[fy],-1,1,n,1 );
cnt[fy] += cnt[fx];
cnt[fx] = 0;
update( cnt[fy],1,1,n,1 );
}
}else{
scanf("%lld",&x);
LL mn = max(1LL,remain-x);
LL mm;
if( x <= contain ){
mm = remain;
}else{
mm = remain - ask(0, x - contain,1,n,1 ) + 1;
}
printf("%lld %lld\n",mn,mm);
}
}
}
return 0;
}