• [HNOI2012]永无乡「线段树合并」


    题目描述

    这不是个链接

    思路分析

    • 这题线段树合并巨好写啊(我是不会告诉你其实是因为我不会写平衡树的)。然后我也没怎么卡常就跑到了洛谷第一页,平衡树脸面何在
    • 排名的值域很小,所以直接对每个节点开一棵关于排名的权值线段树,线段树的下标就是排名,往里面塞个数就行了,这样查询 (k) 小也很简单,因为这时候线段树存储的是排名位于一段区间内的点的个数,直接找个数为 (k) 的那个位置就好了。奥对了,每个叶子节点需要记录一下塞到这个节点的编号。
    • 维护联通性时用并查集简单处理就好了,并查集和线段树同时同向合并,然后好像就没了,成功变成了线段树合并裸题

    (Code)

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define R register
    #define N 100010
    using namespace std;
    inline int read(){
    	int x = 0,f = 1;
    	char ch = getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return x*f;
    }
    int n,m,q,fa[N],id[N<<5],tr[N<<5],ls[N<<5],rs[N<<5],root[N<<5],cnt;
    char op[10];
    int find(int x){
    	return fa[x]==x ? x : (fa[x]=find(fa[x]));
    }
    void modify(int &rt,int l,int r,int pos,int idx){//pos将排名转化为下标
    	if(!rt)rt = ++cnt;
    	if(l==r){
    		id[rt] = idx,tr[rt]++;//id记录编号
    		return;
    	}
    	int mid = (l+r)>>1;
    	if(pos<=mid)modify(ls[rt],l,mid,pos,idx);
    	else modify(rs[rt],mid+1,r,pos,idx);
    	tr[rt] = tr[ls[rt]] + tr[rs[rt]];
    }
    int merge(int a,int b,int l,int r){//线段树合并板子
    	if(!a)return b;
    	if(!b)return a;
    	if(l==r){
    		tr[a] += tr[b];
    		return a;
    	}
    	int mid = (l+r)>>1;
    	ls[a] = merge(ls[a],ls[b],l,mid);
    	rs[a] = merge(rs[a],rs[b],mid+1,r);
    	tr[a] = tr[ls[a]] + tr[rs[a]];
    	return a;
    }
    int query(int rt,int l,int r,int k){//查k小相当于查线段树值刚好等于k的下标(对应的编号)
    	if(tr[rt]<k||!rt)return 0;
    	if(l==r)return id[rt];
    	int mid = (l+r)>>1;
    	if(k<=tr[ls[rt]])return query(ls[rt],l,mid,k);
    	else return query(rs[rt],mid+1,r,k-tr[ls[rt]]);
    }
    int main(){
    	n = read(),m = read();
    	for(R int i = 1;i <= n;i++){
    		fa[i] = i;
    		int x = read();
    		modify(root[i],1,n,x,i);
    	}
    	for(R int i = 1;i <= m;i++){
    		int x = read(),y = read();
    		x = find(x),y = find(y);
    		fa[y] = x;
    		root[x] = merge(root[x],root[y],1,n);
    	}
    	q = read();
    	for(R int i = 1;i <= q;i++){
    		scanf("%s",op);
    		if(op[0]=='B'){
    			int x = read(),y = read();
    			x = find(x),y = find(y);
    			if(x==y)continue;
    			fa[y] = x;
    			root[x] = merge(root[x],root[y],1,n);
    		}else{
    			int x = read(),y = read();
    			x = find(x);
    			int ans = query(root[x],1,n,y);
    			if(!ans){
    				puts("-1");
    				continue;
    			}
    			else printf("%d
    ",ans);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    AC自动机讲解超详细
    区间树Splay——[NOI2005]维护数列
    区间树Splay——[NOI2005]维护数列
    Trie学习总结
    微信小程序刮刮乐
    微信小程序获得高度
    微信小程序多video播放暂停问题
    vue中的问题思考
    vue的开发技巧
    微信小程序消息推送,前端操作
  • 原文地址:https://www.cnblogs.com/hhhhalo/p/13859871.html
Copyright © 2020-2023  润新知