NOI2002银河英雄传说

题目大意:

有一个划分为N列的星际战场,各列依次编号为1,2,……,N。有N艘战舰,也依次编号为1,2,……,N,其中第i号战舰处于第i列。

有M条指令,每条指令格式为以下两种之一:

1.M i j,表示让第i号战舰所在列的全部战舰保持原有顺序,接在第j号战舰的尾部。

2.C i j,表示询问第i号战舰与第j号战舰当前是否处于同一列中,如果在同一列中,它们之间间隔了多少艘战舰。

其中N<=30000,M<=500000.

题目链接:codevs:http://codevs.cn/problem/1540,luogu:https://www.luogu.org/problemnew/show/P1196.

思路:

这题使用的主要数据结构为并查集,将所有战舰放进并查集中来进行查询和合并。

由于数据范围较大,我们需要将并查集的合并进行路径压缩,因为路径压缩会改变它们的原有顺序,因此我们用一个数组d来记录各点到祖先的距离,d数组相减的绝对值即为两数之间的距离。

getfather操作:

在对一艘战舰进行路径压缩的同时,也让d[x]等于它的父亲节点的权值+1.

merge操作:

让x的root等于y的儿子,则d[x]就等于y的集合大小,同时将y的集合大小加上x的集合大小。

代码实现:

int getfather(int x) {
	if(x==fa[x]) return x; //如果该节点为根节点,则返回
	int root=getfather(fa[x]); 
	d[x]+=d[fa[x]]; //子节点到root的距离=父节点到root的距离+1
	return fa[x]=root; //路径压缩
}

void merge(int x,int y) {
	x=getfather(x),y=getfather(y); 
	fa[x]=y;d[x]=size[y]; //让x的父节点等于y,x到原点的距离等于y的长度
	size[y]+=size[x]; //y的长度+=x的长度
}

完整代码如下:

#include<cstdio>
#include<cmath>
using namespace std;
int n,p,q,d[300001];
char c;

int read() {
	int num = 0, flag = 1; char c = getchar();
	for(; c < '0' || c > '9'; c = getchar())
		if(c == '-') flag = 0;
	for(; c >= '0' && c <= '9'; c = getchar())
		num = (num<<3)+(num<<1)+c-48;
	return flag? num: -num;
}

int fa[300001],size[300001];
inline int getfather(int x) {
	if(x==fa[x]) return x;
	int root=getfather(fa[x]);
	d[x]+=d[fa[x]];
	return fa[x]=root;
}

inline void merge(int x,int y) {
	x=getfather(x),y=getfather(y);
	fa[x]=y;d[x]=size[y];
	size[y]+=size[x];
}

int main() {
	n=read();
	for(int i=1;i<=300000;i++) fa[i]=i,size[i]=1;
	for(int i=1;i<=n;i++) {
		c=getchar();p=read();q=read();
		if(c=='M') merge(p,q); 
		if(c=='C') {
			if(getfather(p)!=getfather(q)) puts("-1\n");
			else {
				int ans=abs(d[p]-d[q])+1;
				printf("%d\n",ans-2);
			}
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/chenkainan1023/article/details/81086344