bzoj2733

启发式合并权值线段树

说实话我写的是一个自己yy的东西

首先我对于初始每个联通块开了一个权值线段树

然后对于每个联通块维护了一个vector表示里面有哪些点

对于B操作每次启发式合并vector+权值线段树

对于Q操作在该联通块所属的权值线段树上二分即可

最后注意bzoj不能用auto

/**************************************************************

    Problem: 2733

    User: syh0313

    Language: C++

    Result: Accepted

    Time:2336 ms

    Memory:123988 kb

****************************************************************/

 

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <string>

#include <cmath>

#include <algorithm>

#include <vector>

#define lch a[n].lc

#define rch a[n].rc

#define pb push_back

using namespace std;

const int maxn=100010;

int n,m,v[maxn],root[maxn],be[maxn],topt,id[maxn],nn[maxn],cnt;

vector<int>now[maxn];

struct da{int lc,rc,sum;}a[100*maxn];

inline void updata(int n){a[n].sum=a[lch].sum+a[rch].sum;}

void tree_add(int &n,int l,int r,int lc)

{

    if (!n) n=++topt;

    if (l==r) {a[n].sum++; return;}

    int mid=(l+r)>>1;

    if (lc<=mid) tree_add(lch,l,mid,lc);else tree_add(rch,mid+1,r,lc);

    updata(n);

}  

int qury(int n,int l,int r,int k)

{

    if (l==r) return l;

    int mid=(l+r)>>1;

    if (a[lch].sum>=k) return qury(lch,l,mid,k);else return qury(rch,mid+1,r,k-a[lch].sum);

}

int main()

{

    scanf("%d%d",&n,&m);

    for (int i=1;i<=n;i++) scanf("%d",&v[i]),id[v[i]]=i,be[i]=i,now[i].pb(i);

    for (int i=1;i<=m;i++)

    {

        int xx,yy; scanf("%d%d",&xx,&yy);

        if (be[xx]==be[yy]) continue;

        if (now[be[xx]].size()<now[be[yy]].size())

        {

            cnt=0;

            for (int i=0;i<(int)now[be[xx]].size();i++) nn[++cnt]=now[be[xx]][i];

            for (int i=1;i<=cnt;i++) be[nn[i]]=be[yy],now[be[yy]].pb(nn[i]);

        }

        else

        {

            cnt=0;

            for (int i=0;i<(int)now[be[yy]].size();i++) nn[++cnt]=now[be[yy]][i];

            for (int i=1;i<=cnt;i++) be[nn[i]]=be[xx],now[be[xx]].pb(nn[i]);

        }

    }

    for (int i=1;i<=n;i++)

    if (now[i].size()>0)

    {for (int j=0;j<(int)now[i].size();j++) tree_add(root[i],1,n,v[now[i][j]]);}

    scanf("%d",&m);

    while (m--)

    {

        char op[5]; int x,y; scanf("%s%d%d",op+1,&x,&y);

        if (op[1]=='B')

        {

            if (now[be[x]].size()<now[be[y]].size())

            {

                cnt=0;

                for (int i=0;i<(int)now[be[x]].size();i++) nn[++cnt]=now[be[x]][i];

                for (int i=1;i<=cnt;i++)

                {

                    be[nn[i]]=be[y]; now[be[y]].pb(nn[i]);

                    tree_add(root[be[y]],1,n,v[nn[i]]);

                }

            }

            else

            {

                cnt=0;

                for (int i=0;i<(int)now[be[y]].size();i++) nn[++cnt]=now[be[y]][i];

                for (int i=1;i<=cnt;i++)

                {

                    be[nn[i]]=be[x]; now[be[x]].pb(nn[i]);

                    tree_add(root[be[x]],1,n,v[nn[i]]);

                }

            }

        }

        else

        {

            if (a[root[be[x]]].sum<y) printf("-1\n");

            else

            {

                int k=qury(root[be[x]],1,n,y);

                printf("%d\n",id[k]);

            }

        }

    }

return 0;

}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值