【2019浙江省赛 - A】Vertices in the Pocket(权值线段树下二分,图,思维

网友投稿 716 2022-11-26

【2019浙江省赛 - A】Vertices in the Pocket(权值线段树下二分,图,思维)

【2019浙江省赛 - A】Vertices in the Pocket(权值线段树下二分,图,思维)

题干:

DreamGrid has just found an undirected simple graph with  vertices and no edges (that's to say, it's a graph with  isolated vertices) in his right pocket, where the vertices are numbered from 1 to . Now he would like to perform  operations of the following two types on the graph:

1 a b -- Connect the -th vertex and the -th vertex by an edge. It's guaranteed that before this operation, there does not exist an edge which connects vertex  and  directly.2 k -- Find the answer for the query: What's the minimum and maximum possible number of connected components after adding  new edges to the graph. Note that after adding the  edges, the graph must still be a simple graph, and the query does NOT modify the graph.

Please help DreamGrid find the answer for each operation of the second type. Recall that a simple graph is a graph with no self loops or multiple edges.

Input

There are multiple test cases. The first line of the input is an integer , indicating the number of test cases. For each test case:

The first line contains two integers  and  (, ), indicating the number of vertices and the number of operations.

For the following  lines, the -th line first contains an integer  (), indicating the type of the -th operation.

If , two integers  and  follow (, ), indicating an operation of the first type. It's guaranteed that before this operation, there does not exist an edge which connects vertex  and  directly.If , one integer  follows (), indicating an operation of the second type. It's guaranteed that after adding  edges to the graph, the graph is still possible to be a simple graph.

It's guaranteed that the sum of  in all test cases will not exceed , and the sum of  in all test cases will not exceed .

Output

For each operation of the second type output one line containing two integers separated by a space, indicating the minimum and maximum possible number of connected components in this query.

Sample Input

15 51 1 22 11 1 32 12 3

Sample Output

3 32 31 2

解题报告:

以连通块中元素个数为权值,建立线段树,维护三个变量,分别是cnt(这样的连通块的数量),sum(连通块的总的点数),pfh(各连通块之间点数的平方和)(注意不是和的平方)

cnt代表连通块数量,tot_ret代表当前状态下的图,在不增长连通块个数的前提下,可以加的边数。

所以对于最小值很显然。

对于最大值,首先减去块内连的边,然后去线段树查询剩下的边怎么加,首先填连通块元素大小大的,这样也就是类似查询第k大,查到叶子结点,再判断 块中元素为tree[cur].l的 块 我需要多少个 返回回去,就行了。

AC代码

#include#include#include#include#include#include#include#include#include#include#define F first#define S second#define ll long long#define pb push_back#define pm make_pairusing namespace std;typedef pair PII;const int MAX = 2e5 + 5;int n,q,cnt,f[MAX];ll tot_ret,ret[MAX],num[MAX];int getf(int v) { return f[v] == v ? v : f[v] = getf(f[v]);}struct TREE { int l,r; ll cnt,sum; ll pfh;} tree[MAX<<2];void pushup(int cur) { tree[cur]-t = tree[cur*2]-t + tree[cur*2+1]-t; tree[cur].sum = tree[cur*2].sum + tree[cur*2+1].sum; tree[cur].pfh = tree[cur*2].pfh + tree[cur*2+1].pfh;}void build(int l,int r,int cur) { tree[cur].l=l;tree[cur].r=r; if(l == r) { tree[cur].pfh = tree[cur].sum = tree[cur]-t = (l==1?n:0); return; } int mid = (l+r)>>1; build(l,mid,cur*2); build(mid+1,r,cur*2+1); pushup(cur);}void update(int cur,ll tar,ll val) { if(tree[cur].l == tree[cur].r) { tree[cur]-t += val;// 连通块元素数量为tar的连通块的数量。 tree[cur].sum += val*tar; tree[cur].pfh += val*tar*tar; return; } int mid = (tree[cur].l+tree[cur].r)>>1; if(tar<=mid) update(cur*2,tar,val); else update(cur*2+1,tar,val); pushup(cur);}ll query(int cur,ll k,int sum) { if(tree[cur].l == tree[cur].r) { ll l=1,r=tree[cur]-t,mid; mid=(l+r)>>1; while(l>1; if(mid*(mid-1)/2*tree[cur].l*tree[cur].l + mid*tree[cur].l*sum >= k) r=mid; else l = mid+1; } return l; } int mid = (tree[cur].l+tree[cur].r)>>1; ll tmp = (tree[cur*2+1].sum * tree[cur*2+1].sum - tree[cur*2+1].pfh)/2 + tree[cur*2+1].sum * sum ; if(tmp < k) return tree[cur*2+1]-t + query(cur*2,k-tmp,sum+tree[cur*2+1].sum); else return query(cur*2+1,k,sum);}ll cal(ll k) { if(k<=tot_ret) return cnt; k-=tot_ret; ll tmp = query(1,k,0); return cnt-tmp+1;}int main(){ int t; cin>>t; while(t--) { scanf("%d%d",&n,&q); for(int i = 1; i<=n; i++) f[i] = i,num[i] = 1,ret[i] = 0; tot_ret=0,cnt=n;//cnt记录连通块数 build(1,n,1); while(q--) { int op; scanf("%d",&op); if(op == 1) { int a,b; scanf("%d%d",&a,&b); a=getf(a),b=getf(b); if(a==b) { ret[a]--; tot_ret--; continue; } if(num[a] > num[b]) swap(a,b);//让b是边多的 update(1,num[a],-1); update(1,num[b],-1); f[a]=b;//按秩合并到大集合 tot_ret += num[a]*num[b]-1;cnt--; ret[b]=ret[a]+ret[b] + num[a]*num[b]-1; num[b]+=num[a]; ret[a]=num[a]=0; update(1,num[b],1); } else { ll k; scanf("%lld",&k); ll minn = max(1LL,cnt-k); ll maxx = cal(k); printf("%lld %lld\n",minn,maxx); } } } return 0 ;}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:PL/SQL函数操作实例与说明
下一篇:*【CodeForces - 1150D】Three Religions(dp,预处理,思维)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~