bzoj2733: [HNOI2012]永無鄉(並查集+主席樹)

Hanks_o發表於2018-04-16

題目傳送門

解法:
跟3545解法差不多。
https://blog.csdn.net/hanks_o/article/details/79903342
這道題還沒了邊權。
直接連通。

那麼並查集。
據說要啟發式。
小的向大的合併。
然後就沒了?

程式碼實現:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct node {int lc,rc,c;}t[2100000];int cnt,rt[110000];
void build(int &u,int l,int r,int p) {
    if(u==0)u=++cnt;t[u].c++;
    if(l==r)return ;int mid=(l+r)/2;
    if(p<=mid)build(t[u].lc,l,mid,p);
    else build(t[u].rc,mid+1,r,p);
}
void Merge(int &u1,int u2) {
    if(u1==0) {u1=u2;return ;}if(u2==0)return ;t[u1].c+=t[u2].c;
    Merge(t[u1].lc,t[u2].lc);Merge(t[u1].rc,t[u2].rc);
}
int fa[110000];int findfa(int x) {if(fa[x]!=x)fa[x]=findfa(fa[x]);return fa[x];}
int s[110000];
int find(int u,int l,int r,int k) {
    if(t[u].c==0)return -1;
    if(l==r)return s[l];int mid=(l+r)/2,c=t[t[u].lc].c;
    if(k<=c)return find(t[u].lc,l,mid,k);
    else return find(t[u].rc,mid+1,r,k-c);
}
int main() {
    int n,m;scanf("%d%d",&n,&m);cnt=0;
    for(int i=1;i<=n;i++){int x;scanf("%d",&x);build(rt[i],1,n,x);s[x]=i;}
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++) {
        int x,y;scanf("%d%d",&x,&y);
        int xx=findfa(x),yy=findfa(y);
        if(xx!=yy) {
            if(t[rt[xx]].c<=t[rt[yy]].c) {fa[xx]=yy;Merge(rt[yy],rt[xx]);}
            else {fa[yy]=xx;Merge(rt[xx],rt[yy]);}
        }
    }
    int Q;scanf("%d",&Q);
    while(Q--) {
        char s[5];int x,y;scanf("%s%d%d",s+1,&x,&y);
        if(s[1]=='Q') printf("%d\n",find(rt[findfa(x)],1,n,y));
        else {
            int xx=findfa(x),yy=findfa(y);
            if(xx!=yy) {
                if(t[rt[xx]].c<=t[rt[yy]].c) {fa[xx]=yy;Merge(rt[yy],rt[xx]);}
                else {fa[yy]=xx;Merge(rt[xx],rt[yy]);}
            }
        }
    }
    return 0;
}

相關文章