POJ2985 (线段树+并查集)

题目链接: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));
            }
        }
    }
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值