题目链接:https://cn.vjudge.net/problem/ZOJ-4100
题意:n个点,q次操作,1,x,y链接x和y,2,x加上x条边,问最少和最多形成的联通块数
题解:并查集把联通的点处理下,并记录下每个联通块的点的数目,记录下所有联通块一共还需要多少条边rt_val,使得各自完全联通,以联通块点的个数建立线段树,维护下[l,r] 联通块的数目 联通块点的总和 每个连通块都是完全图的情况下还需要多少条边使得全部的点形成完全图,计算最少很好计算,计算最多时,加入k条边,如果rt_val>=k那么目前的联通块数不变,否则,一定会导致联通块之间连接,那么肯定是连接两个较大的联通块带来的效果更好,然后线段树从大到小查询下,到达哪个位置pos时,符合条件,因为联通块点数为pos的可能有很多,然后再二分判断下pos点数的联通块需要多少即可,一定要主要初始化!初始化!初始化!初始化!初始化!初始化!初始化!初始化!初始化!初始化!初始化!初始化!初始化!初始化!初始化!初始化!
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
struct node{
int l,r;
ll sum; // 总人数
ll cnt; // 联通块数
ll val; // 还需要加多少
}tree[N<<2];
int n,q;
int f[N];
ll num[N];
ll rt;
ll rt_val;
void init()
{
for(int i=0;i<=n;i++)
{
f[i]=i;
num[i]=1;
}
rt=n;
rt_val=0;
}
int fath(int x)
{
return x==f[x]?x:f[x]=fath(f[x]);
}
void pushup(int cur)
{
tree[cur].cnt=tree[cur<<1].cnt+tree[cur<<1|1].cnt;
tree[cur].sum=tree[cur<<1].sum+tree[cur<<1|1].sum;
tree[cur].val=tree[cur<<1].sum*tree[cur<<1|1].sum+tree[cur<<1].val+tree[cur<<1|1].val;
}
void build(int l,int r,int cur)
{
tree[cur].l=l;
tree[cur].r=r;
tree[cur].cnt=0;
tree[cur].sum=0;
tree[cur].val=0;
if(l==r)
{
if(l==1)
{
tree[cur].cnt=n;
tree[cur].sum=n;
tree[cur].val=(ll)n*(n-1)/2;
}
return;
}
int mid=(r+l)>>1;
build(l,mid,cur<<1);
build(mid+1,r,cur<<1|1);
pushup(cur);
}
void update(int pos,int cur,int v)
{
if(tree[cur].l==tree[cur].r)
{
tree[cur].cnt+=v;
tree[cur].sum+=v*tree[cur].l;
tree[cur].val=tree[cur].sum*(tree[cur].sum-1)/2-(ll)tree[cur].l*(tree[cur].l-1)/2*tree[cur].cnt;
return;
}
if(pos<=tree[cur<<1].r) update(pos,cur<<1,v);
else update(pos,cur<<1|1,v);
pushup(cur);
}
ll tmp,pos,possum; // 已选择节点数量 最后位置 最后位置的数目
ll tmprt; // 已选择的联通块数目
ll k;
void query(int cur)
{
if(tree[cur].l==tree[cur].r)
{
pos=tree[cur].l;
possum=tree[cur].cnt;
return;
}
if(tmp*tree[cur<<1|1].sum+tree[cur<<1|1].val>=k) query(cur<<1|1);
else
{
k-=tmp*tree[cur<<1|1].sum+tree[cur<<1|1].val;
tmp+=tree[cur<<1|1].sum;
tmprt+=tree[cur<<1|1].cnt;
query(cur<<1);
}
}
int main()
{
int T;
int xx,yy;
int op,x,y;
ll minn,maxx;
ll l,r,mid;
ll cnt;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&q);
init();
build(1,n,1);
while(q--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%d",&x,&y);
xx=fath(x);
yy=fath(y);
if(xx==yy)
{
rt_val--;
continue;
}
rt--;
rt_val+=num[xx]*num[yy]-1;
update(num[xx],1,-1);
update(num[yy],1,-1);
f[xx]=yy;
num[yy]+=num[xx];
update(num[yy],1,1);
}
else
{
scanf("%lld",&k);
minn=max(1LL,rt-k);
if(rt_val>=k)
{
maxx=rt;
}
else
{
tmp=0;
tmprt=0;
k-=rt_val;
query(1);
l=1,r=possum;
while(l<=r)
{
mid=(r+l)>>1;
cnt=pos*mid*(pos*mid-1)/2-pos*(pos-1)/2*mid+tmp*pos*mid;
if(cnt>=k)
{
maxx=rt-(tmprt+mid-1);
r=mid-1;
}
else l=mid+1;
}
}
printf("%lld %lld\n",minn,maxx);
}
}
}
return 0;
}