zoukankan      html  css  js  c++  java
  • 【题解】永无乡

    (Question)

    题目大意:给(n)个点及其权值,每次并两个点,会形成一些联通块,求一个点所在联通块的权值第(k)小的点的编号。

    读起来比较绕口,但实质就那么几个:插入,合并,求第(K)小。

    我们可以想到用线段树合并。而维护连通性,可以用那个代码量小,短小精悍的数据结构:并查集。

    具体来说,当合并(x,y)的时候,找到它们的并查集的(root),以总体来合并,方便维护信息。合并完之后,将两个点的并查集(root)重置一下,记为总的那个根就行。

    对于查询,我们可以在树上二分求(k)小。无解当且仅当它所在联通块的节点数小于(k)

    注意输出是输出点的编号,维护一个映射:(f:v->Num)即可。(Num)是编号,(v)是权值。

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=500000;
    int rt[MAXN],ch[MAXN<<5][2],rub[MAXN<<5],n,m,tot,cnt;
    int v[MAXN],f[MAXN],Q;
    int val[MAXN<<5],vb[MAXN];
    char c[5];
    inline int build(){return (cnt?rub[cnt--]:++tot);}
    void del(int x){
    	ch[x][0]=ch[x][1]=val[x]=0;
    	rub[++cnt]=x;return;
    } 
    void modify(int &p,int l,int r,int pos,int vv){
    	if(!p)p=build();
    	val[p]+=vv;
    	if(l==r)return;
    	int mid=l+r>>1;
    	if(pos<=mid)modify(ch[p][0],l,mid,pos,vv);
    	else modify(ch[p][1],mid+1,r,pos,vv);
    }
    int query(int p,int l,int r,int ql,int qr){
    	if(l>qr||r<ql)return 0;
    	if(l>=ql&&r<=qr)return val[p];
    	int mid=l+r>>1;
    	return query(ch[p][0],l,mid,ql,qr)+query(ch[p][1],mid+1,r,ql,qr);
    }
    int kth(int p,int l,int r,int k){
    	if(l==r)return l;
    	int mid=l+r>>1;
    	if(val[ch[p][0]]>=k)return kth(ch[p][0],l,mid,k);
    	else return kth(ch[p][1],mid+1,r,k-val[ch[p][0]]);
    }
    int merge(int x,int y){
    	if(!x||!y)return x+y;
    	val[x]+=val[y];
    	ch[x][0]=merge(ch[x][0],ch[y][0]);
    	ch[x][1]=merge(ch[x][1],ch[y][1]);
    	del(y);return x;
    }
    inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i)scanf("%d",&v[i]),f[i]=i,vb[v[i]]=i;
    	for(int i=1;i<=n;++i)modify(rt[i],1,n,v[i],1);
    	for(int i=1;i<=m;++i){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		x=find(x),y=find(y);
    		if(x==y)continue; 
    		rt[x]=merge(rt[x],rt[y]);f[y]=x;
    	}
    	scanf("%d",&Q);
    	while(Q--){
    		scanf("%s",c);
    		int x,y;
    		scanf("%d%d",&x,&y);
    		if(c[0]=='B'){
    			int X=x,Y=y;
    			X=find(X),Y=find(Y);
    			if(X==Y)continue;
    			rt[X]=merge(rt[X],rt[Y]);f[Y]=X;
    		}
    		else{
    			int G=v[x];
    			x=find(x);
    			if(y>query(rt[x],1,n,1,n)){
    				cout<<-1<<endl;
    				continue;
    			}
    			printf("%d
    ",vb[kth(rt[x],1,n,y)]);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    实现不限层级的Element的NavMenu
    vue_插槽的理解和使用
    vue 动态修改路由参数
    什么是回流,什么是重绘,有什么区别?
    Vue路由获取路由参数
    【前端图表】echarts实现散点图x轴时间轴
    为什么 char 数组比 String 更适合存储密码?
    MySQL 日期时间类型怎么选?千万不要乱用!
    MySQL not exists 真的不走索引么?
    谷歌开源的代码评审规范,值得借鉴!
  • 原文地址:https://www.cnblogs.com/h-lka/p/12562065.html
Copyright © 2011-2022 走看看