【洛谷P3224】永无乡

题目

题目链接:https://www.luogu.com.cn/problem/P3224
永无乡包含 \(n\) 座岛,编号从 \(1\)\(n\) ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 \(n\) 座岛排名,名次用 \(1\)\(n\) 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 \(a\) 出发经过若干座(含 \(0\) 座)桥可以 到达岛 \(b\) ,则称岛 \(a\) 和岛 \(b\) 是连通的。
现在有两种操作:

  • \(B\ x\ y\) 表示在岛 \(x\) 与岛 \(y\) 之间修建一座新桥。
  • \(Q\ x\ k\) 表示询问当前与岛 \(x\) 连通的所有岛中第 \(k\) 重要的是哪座岛,即所有与岛 \(x\) 连通的岛中重要度排名第 \(k\) 小的岛是哪座,请你输出那个岛的编号。

思路

对于每一个点建立一棵权值线段树,区间\([l,r]\)维护在该点所连通的点中,有所少个排名在\([l,r]\)之间的岛。
那么就十分显然了,对于\(B\)操作就线段树合并,\(Q\)操作就权值线段树查询第\(k\)小即可。
注意如果并查集是\(x\)并到\(y\),那么线段树也要\(x\)并到\(y\)。当加入的一条边连接的两个点处于同一集合内就不用再合并了。
时间复杂度\(O(q\log n)\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=100010;
int n,m,father[N],rt[N],rk[N];
char ch;

int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]);
}

struct Treenode
{
	int lc,rc,cnt;
};

struct Tree
{
	Treenode tree[N*20];
	int tot;
	
	void pushup(int x)
	{
		tree[x].cnt=tree[tree[x].lc].cnt+tree[tree[x].rc].cnt;
	}
	
	void update(int &x,int l,int r,int k)
	{
		if (!x) x=++tot;
		if (l==r)
		{
			tree[x].cnt=1;
			return;
		}
		int mid=(l+r)>>1;
		if (k<=mid) update(tree[x].lc,l,mid,k);
			else update(tree[x].rc,mid+1,r,k);
		pushup(x);
	}
	
	void merge(int &x,int y)
	{
		if (!x || !y) x+=y;
		else
		{
			merge(tree[x].lc,tree[y].lc);
			merge(tree[x].rc,tree[y].rc);
			pushup(x);
		}
	}
	
	int ask(int x,int l,int r,int k)
	{
		if (l==r)
		{
			if (k==1 && tree[x].cnt==1) return rk[l];
				else return -1;
		}
		int mid=(l+r)>>1;
		if (tree[tree[x].lc].cnt>=k)
			return ask(tree[x].lc,l,mid,k);
		else
			return ask(tree[x].rc,mid+1,r,k-tree[tree[x].lc].cnt);
	}
}tree;

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1,x;i<=n;i++)
	{
		scanf("%d",&x);
		tree.update(rt[i],1,n,x);
		rk[x]=father[i]=i;
	}
	for (int i=1,x,y;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		x=find(x); y=find(y);
		if (x!=y)
		{
			father[x]=y;
			tree.merge(rt[y],rt[x]);
		}
	}
	scanf("%d",&m);
	while (m--)
	{
		while (ch=getchar()) if (ch=='B'||ch=='Q') break;
		int x,y;
		scanf("%d%d",&x,&y);
		if (ch=='B')
		{
			x=find(x); y=find(y);
			if (x!=y)
			{
				father[x]=y;
				tree.merge(rt[y],rt[x]);
			}
		}
		else
		{
			x=find(x);
			printf("%d\n",tree.ask(rt[x],1,n,y));
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/12243048.html