P3224 [HNOI2012]永无乡(线段树合并模板简化版)

本文解析了P3224题目,介绍了如何使用线段树结构与并查集相结合来解决合并操作的问题,重点讲解了数据结构的运用和并查集在维护合并根节点中的关键作用,适合对模板题有一定理解的基础程序员阅读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P3224 [HNOI2012]永无乡

算是线段树合并的模板题,不过好像用fhq做会更快(不管了)

用一个并查集维护合并的根,注意合并的时候,并查集合并要与线段树合并方向要一致。

算是很模板的一道题

#include <bits/stdc++.h>
#define inf 0x7fffffff
#define ll long long
#define int long long
//#define double long double
#define re register int
#define void inline void
#define eps 1e-8
//#define mod 1e9+7
#define ls(p) p<<1
#define rs(p) p<<1|1
#define pi acos(-1.0)
#define pb push_back
#define P pair < int , int >
#define mk make_pair
using namespace std;
const int mod=1e9+7;
const int M=1e8+5;
const int N=8e6+5;//?????????? 4e8
struct nod
{
	int l,r,sum,id;
}e[N];
int n,fa[N],m,q;
int rt[N],tot;
int get(int x){  return fa[x]==x?x:fa[x]=get(fa[x]);  }
void pushup(int p)
{
	e[p].sum=e[e[p].l].sum+e[e[p].r].sum;
}
int insert(int p,int l,int r,int pos,int root)
{
	if(!p)  p=++tot;
	e[p].sum++;
	if(l==r)
	{
		e[p].id=root;
		return p;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)  e[p].l=insert(e[p].l,l,mid,pos,root);
	else  e[p].r=insert(e[p].r,mid+1,r,pos,root);
	return p;
}
int merge(int p1,int p2,int l,int r)
{
	if(!p1||!p2)  return p1+p2;
	if(l==r)
	{
		if(e[p2].id)
		{
			e[p1].sum+=e[p2].sum;
			e[p1].id=e[p2].id;
		}
		return p1;
	}
	int mid=(l+r)>>1;
	e[p1].l=merge(e[p1].l,e[p2].l,l,mid);
	e[p1].r=merge(e[p1].r,e[p2].r,mid+1,r);
	pushup(p1);
	return p1;
}
int ask(int p,int l,int r,int k)
{
	if(k>e[p].sum||!p)  return -1;
	if(l==r)  return e[p].id;
	int t=e[e[p].l].sum;
	int mid=(l+r)>>1;
	if(k<=t)  return ask(e[p].l,l,mid,k);
	return ask(e[p].r,mid+1,r,k-t);
}
void solve()
{
	cin>>n>>m;
	for(re i=1;i<=n;i++)
	{
		int x;
		scanf("%lld",&x);
		fa[i]=i;
		rt[i]=insert(rt[i],1,n,x,i);
	}
	for(re i=1;i<=m;i++)
	{
		int x,y;
		scanf("%lld%lld",&x,&y);
		x=get(x),y=get(y);
		if(x==y)  continue;
		fa[y]=x;
		rt[x]=merge(rt[x],rt[y],1,n);
	}
	cin>>q;
	while(q--)
	{
		char op[5];
		int x,y;
		scanf("%s%lld%lld",op,&x,&y);
		x=get(x);
		if(op[0]=='Q')  printf("%lld\n",ask(rt[x],1,n,y));
		else
		{
			y=get(y);
			if(x==y)  continue;
			rt[x]=merge(rt[x],rt[y],1,n);
			fa[y]=x;
		}
	}
}
signed main()
{
//	freopen("P1505_1.txt", "r", stdin);
//	freopen("Aout.txt", "w", stdout);
    int T=1;
//    cin>>T;
    for(int index=1;index<=T;index++) 
    {
//    	printf("Case %d:\n",index);
        solve();
//        puts("");
    }
    return 0;
}
/*a 





*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值