题目链接:http://poj.org/problem?id=2985
题意:输入n(猫的数量)和m(操作的总数),求分组以及合并操作后,第K大的组的规模大小
解题思路:并查集+线段树
- Source Code
/*以分组的规模作为划分线段的标准 有n只猫则可能存在的分组中规模最大的为n,规模最小的为1 tree[k]表示规模为tree[k].left---tree[k].right的线段 tree[k].sum表示规模在该范围内存在的组数的个数 线段树+并查集 求规模第K大的集合的规模*/ #include <iostream> #include "stdio.h" #include "stdlib.h" #include "string.h" #define N 200005 using namespace std; struct node { int sum,left,right; }; void union_(int x,int y); int find_(int x); void update(int k,int pos,int change); int n,m,father[N],sums[N];//father[i]表示i的祖先,sums[i]表示第i只猫所在集合的猫的数量 struct node tree[N<<2]; int find_(int x)//找到第x只猫的祖先 { if(father[x]==x) return x; return father[x]=find_(father[x]); } void union_(int x,int y)//如果两只猫的祖先不同 将其所在的集合合并 { int a=find_(x),b=find_(y); if(a==b) return; father[a]=b;//将两只猫所在的集合合并 update(1,sums[a],-1);// 集合规模包含sums(a)的总数量-1 update(1,sums[b],-1);// 集合规模包含sums(b)的总数量-1 update(1,sums[a]+sums[b],1);// 集合规模包含sums(a)+sums(b)的总数量+1 sums[b]+=sums[a];//合并后新的集合的猫的总数更新 } void build(int k,int l,int r)//建立规模为l-r的线段树 { tree[k].left=l;tree[k].right=r;tree[k].sum=0; if(l==r) { if(l==1) tree[k].sum=n; return; } int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } void update(int k,int pos,int change)//更改线段树 { int l=tree[k].left,r=tree[k].right; if(l==r) { tree[k].sum+=change; return; } int mid=(l+r)>>1; if(pos<=mid) update(k<<1,pos,change); else update(k<<1|1,pos,change); tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum; } int find_sth_size(int k,int s)//找到规模第s大的集合的规模 { int l=tree[k].left,r=tree[k].right; if(l==r) return l; if(s<=tree[k<<1|1].sum) find_sth_size(k<<1|1,s); else find_sth_size(k<<1,s-tree[k<<1|1].sum); } int main(void) { scanf("%d%d",&n,&m); build(1,1,n); tree[1].sum=n; for(int i=1;i<=n;i++) { father[i]=i; sums[i]=1; } for(int i=0;i<m;i++) { int order; scanf("%d",&order); if(order==0){ int a,b; scanf("%d%d",&a,&b); union_(a,b); } else { int a; scanf("%d",&a); printf("%d\n",find_sth_size(1,a)); } } }