• [HNOI2012]永无乡


    题目大意

    给出一张\(n\)个点的图,给出初始的联通信息

    每个点都有一个独一无二的权值(在\(1 \sim n\)中)

    接下来有两种操作:

    \(Q\)操作:询问\(A\)所在联通块中权值第\(k\)大(不存在输出\(-1\)

    \(B\)操作:联通\(X,Y\)

    解题思路

    一眼是平衡树裸题呀,但本文选择用动态开点线段树合并做,另外用并查集维护一下连通性

    \(i\)棵线段树维护了\(i\)所在的联通块中权值在\(l \sim r\)的结点总数\(size\)

    定义:\(find(x)\)\(x\)在并查集中的最远祖先(并查集基本操作)

    我们约定\(i\)所在的联通块的信息保存在第\(find(i)\)棵线段树上

    这意味着一个联通块有唯一确定的线段树在维护它

    合并操作:

    首先判断\(x,y\)是否已经联通(是否在并查集同一个最远祖先下),若已经联通就跳出

    否则合并第\(find(x)\)\(find(y)\)棵线段树,并合并在\(root[find(x)]\)下(相当于把第\(find(y)\)棵线段树的信息附加到第\(find(x)\)棵上)

    然后维护并查集的信息(常规\(Union\)操作)

    询问操作:

    因为\(A\)所在联通块信息保存在第\(find(A)\)棵线段树上,所以在第\(find(A)\)棵线段树上查找第\(k\)大即可(利用线段树维护的\(size\)信息)

    #include<iostream>
    #include<cstdio>
    
    int n,m,imp[200000],reimp[200000];
    char opt;
    int x,y;
    
    int B[200000];
    
    struct node{
    	int L,R,size;
    }N[10000000];
    int tot,root[200000];
    
    void insert(int &st,int k,int l,int r){
    	if (l>k||r<k) return;
    	if (!st) st=++tot;
    	if (l==r){N[st].size=1;return;}
    	int mdl=(l+r)>>1;
    	insert(N[st].L,k,l,mdl);
    	insert(N[st].R,k,mdl+1,r);
    	N[st].size=1;
    }
    
    int merge(int nowL,int nowR,int l,int r){
    	if (!(nowL&&nowR)) return nowL|nowR;
    	if (l>r) return 0;
    	N[nowL].size+=N[nowR].size;
    	int mdl=(l+r)>>1;
    	N[nowL].L=merge(N[nowL].L,N[nowR].L,l,mdl);
    	N[nowL].R=merge(N[nowL].R,N[nowR].R,mdl+1,r);
    	return nowL;
    }
    
    int query(int now,int k,int l,int r){
    	if (l==r) return reimp[l];
    	int rank=N[N[now].L].size;
    	int mdl=(l+r)>>1;
    	if (k<=rank) return query(N[now].L,k,l,mdl);
    	else return query(N[now].R,k-rank,mdl+1,r);
    }
    
    int find(int k){return (k==B[k])?(k):(B[k]=find(B[k]));}
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++){
    		scanf("%d",&imp[i]);
    		insert(root[i],imp[i],1,n+1);
    		reimp[imp[i]]=i;
    	}
    	for (int i=1;i<=n;i++) B[i]=i;
    	for (int i=1;i<=m;i++){
    		scanf("%d%d",&x,&y);
    		if (find(x)==find(y)) continue;
    		root[find(y)]=merge(root[find(y)],root[find(x)],1,n+1);
    		B[find(x)]=find(y);
    	}
    	scanf("%d\n",&m);
    	for (int i=1;i<=m;i++){
    		scanf("%c%d%d\n",&opt,&x,&y);
    		if (opt=='Q'){
    			int ans=query(root[find(x)],y,1,n+1);
    			if (!ans) puts("-1");
    			else printf("%d\n",ans);
    		}
    		else{
    			if (find(x)==find(y)) continue;
    			root[find(y)]=merge(root[find(y)],root[find(x)],1,n+1);
    			B[find(x)]=find(y);
    		}
    	}
    }
    
  • 相关阅读:
    js变量的作用域问题
    HTML网页公用头部与尾部的一些方法
    拖动对象ondrag
    子div设置float后导致父div无法自动撑开的问题
    js像素运算问题
    js自动轮播图片的两种循环方法(原创)
    display:none显示和隐藏
    Linux shell脚本基础学习详细介绍(完整版)一
    java技术思维导图(转载)
    VIM编辑命令的技巧
  • 原文地址:https://www.cnblogs.com/ytxytx/p/9441019.html
Copyright © 2020-2023  润新知