哈尔滨理工大学第八届程序设计竞赛 B GT's Dream 【树状数组】

GT’s Dream

Time Limit: 2000/1000 MS(Java/Others)    Memory Limit: 256000/256000K (Java/Others)

Problem Description

在现实中认了无数师傅却毫无长进的GT在梦中成为了某武侠世界的神。在这个世界中初始有n个人,他们各成一派。作为世界神GT总共会进行m次操作,每次操作有如下两种情况

1 x y表示x所在的帮派吞并了y所在的帮派,若xy本来就处于同一个帮派则该操作无效。

2 k表示GT想要知道当前第k大的帮派有多少人,若当前帮派数量少于k个则输出-1

 

 

Input

第一行输入一个TT<=10)表示有T组测试数据。

每组数据的第一行包含两个数n,mn<=100000,m<=300000)表示有n个人,m次操作

接下来m行包含每一次的操作情况。

 

 

Output

对于每一个2操作输出当前第k大的帮派有多少人。

 

 

Sample Input

1

5 4

1 2 4

1 3 4

2 1

2 2

 

 

Sample Output

3

1


【题意】

RT

【思路】

对于合并操作,显然我们可以用并查集。关键在于对第k大的查询。

考虑到每个帮派的人数不会超过100000,我们想到去储存每一个时刻大小为某一人数的帮派个数。

由于每次查询暴力遍历显然会超时,我们想到用线状数组来更新和查询值。

合并时,两个帮派合并成一个,那么可以记为一个帮派的人数为两个帮派之和,还有一个为0,依据此更新数组。

查询时,由于我们用树状数组直接只能查询前缀和,而我们是要从大往小找,于是我们想到用n-前缀和去实现,然后二分即可。

【PS】树状数组下标要从1开始,所以所有操作时都加1处理

#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn = 100005;
const ll mod = 1e9+7;
const ll INF = 1e18;
const double eps = 1e-6;

int n,m;
int a[maxn];
int pre[maxn];
int num[maxn];
char s[maxn];

int tree[maxn];

int lowbit(int x)
{
    return x&(-x);
}

int query(int pos)
{
    int ans=0;
    while(pos>0)
    {
        ans+=tree[pos];
        pos-=lowbit(pos);
    }
    return ans;
}

void add(int pos,int val)
{
    while(pos<maxn)
    {
        tree[pos]+=val;
        pos+=lowbit(pos);
    }
}


int find(int x)
{
    int t,r=x;
    while(pre[x]!=x)
    {
        x=pre[x];
    }
    while(r!=x)
    {
        t=pre[r];
        pre[r]=x;
        r=t;
    }
    return x;
}

void join(int a,int b)
{
    int A=find(a);
    int B=find(b);
    if(A==B) return;
    add(num[A]+1,-1);
    num[A]+=num[B];
    add(num[B]+1,-1);
    add(num[A]+1,1);
    num[B]=0;
    add(num[B]+1,1);
    pre[B]=A;
}


int main()
{
    rush()
    {
        mst(tree,0);
        scanf("%d%d",&n,&m);
        for(int i=0; i<=n; i++) pre[i]=i,num[i]=1;
        add(2,n);
        for(int i=0; i<m; i++)
        {
            int op;
            int x,y;
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d%d",&x,&y);
                join(x,y);
            }
            else
            {
                scanf("%d",&x);
                int ans=0;
                int l=1,r=n+1;
                while(l<=r)
                {
                    int mid=(l+r)/2;
                    if(n-query(mid)<x)
                    {
                        ans=mid;
                        r=mid-1;
                    }
                    else l=mid+1;
                }
                if(ans==1) puts("-1");
                else printf("%d\n",ans-1);
            }
        }
    }
    return 0;
}

    原文作者:B树
    原文地址: https://blog.csdn.net/my_sunshine26/article/details/79685759
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞