P3224 [HNOI2012]永无乡

题面

一开始,每个集合只有一个岛,对于一个集合,我们建一棵线段树,当连边的时候,我们先判断一下是不是已经在一个集合,然后合并线段树,查询的时候查询所在集合的线段树即可,若\(k\)大于集合元素数,输出\(-1\)

那么怎么维护集合呢?并查集啊

做完了

下面是代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#define ll long long
#define gc getchar
#define maxn 100005
using namespace std;

inline ll read(){
    ll a=0;int f=0;char p=gc();
    while(!isdigit(p)){f|=p=='-';p=gc();}
    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
    return f?-a:a;
}int n,m,q,a[maxn],c[maxn];

int f[maxn],sz[maxn];
int find(int i){
    if(f[i]!=i)f[i]=find(f[i]);
    return f[i];
}

struct ahaha{
    int v,ch[2];
}t[maxn*40];int top,rt[maxn];
#define lc t[i].ch[0]
#define rc t[i].ch[1]
void update(int &i,int l,int r,int k){
    if(!i)i=++top;++t[i].v;
    if(l==r)return;
    int m=l+r>>1;
    if(k<=m)update(lc,l,m,k);
    else update(rc,m+1,r,k);
}
int query(int i,int l,int r,int k){
    if(l==r)return c[l];
    int m=l+r>>1,v=t[lc].v;
    if(k<=v)return query(lc,l,m,k);
    else return query(rc,m+1,r,k-v);
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    int i=++top;t[i].v=t[x].v+t[y].v;
    lc=merge(t[x].ch[0],t[y].ch[0]);
    rc=merge(t[x].ch[1],t[y].ch[1]);
    return i;
}

inline void solve_1(){
    int x=read(),y=read();
    int fx=find(x),fy=find(y);
    if(fx==fy)return;
    f[fy]=fx,sz[fx]+=sz[fy];
    rt[fx]=merge(rt[fx],rt[fy]);
}
inline void solve_2(){
    int x=read(),k=read();
    int fx=find(x);
    if(sz[fx]<k){
        puts("-1");
        return;
    }
    printf("%d\n",query(rt[fx],1,n,k));
}

char s[5];
int main(){
    n=read();m=read();
    for(int i=1;i<=n;++i){
        a[i]=read(),f[i]=i,sz[i]=1;c[a[i]]=i;
        update(rt[i],1,n,a[i]);
    }
    for(int i=1;i<=m;++i)
        solve_1();
    q=read();
    while(q--){
        scanf("%s",s+1);
        switch(s[1]){
            case 'B':solve_1();break;
            case 'Q':solve_2();break;
        }
    }
    return 0;
}
posted @ 2019-02-25 10:46 子谦。 阅读(...) 评论(...) 编辑 收藏
Live2D
//雪