「NOI2002」 银河英雄传说

题意

有两个名为xxx的神奇数据结构,xxx AAA 有最多 300003000030000 个点,开始每行都只有一个点。

有时会给出合并指令,格式为 MMM iii jjj,每次都将处在第iii个点所在行整体移到第 jjj 个点所在行的最后。

有时会给出查询指令,格式为 CCC iii jjj,询问。

分析

对于这道题,首先不要被它的NOI身份(就是个古早的t1)和较长的描述(实际也不长)吓到。

仔细观察,因为要用并查集,所以可以把每一行的第一个点就可以看成根节点,这就具备了并查集的基本形状。

可以把问题分成几个小问题:

  1. 记录每个点在哪一行:因为这是并查集的题,所以可以用并查集实现。

  2. 计算同一行内两个点的距离:执行 findfindfind 操作时,因为要对 preprepre 数组进行路径压缩(不然容易炸空间&时间),所以较难直接用 preprepre 求解,再开一个 disdisdis 数组,直接记录每一个点到它的根节点的距离,需要时直接做差求绝对值就好了。

  3. 实现题目中的合并操作:一般并查集的合并操作是直接合并每棵树的根节点(改变 preprepre 数组),不会改变其他如 disdisdis 的值,所以可以用 numnumnum 数组,记录每一排的节点数,每次合并时直接在 disidis_idisi 上加 numjnum_jnumj,就可以对同一排内的点进行顺序区分。

可以看到,每次 disidis_idisi 变化时,都会加上 numjnum_jnumj,但 prefind(i)pre_{find(i)}prefind(i) 可以直接连到 prefind(j)pre_{find(j)}prefind(j) 上,这样就实现了合并。

那么查询其实就是求从 iiijjj 的节点数,因为有 disdisdis 记录两点到第一个点的距离,就可以直接转化为求 abs(disi−disj)abs(dis_i-dis_j)abs(disidisj)

end.

Code

#include<bits/stdc++.h>
using namespace std;
char f;
int t,a,b,x,y,pre[30001],dis[30001],num[30001];
int find(int x){//带权并查集的路径压缩find
	if (pre[x]==x)return x;
	int sum=find(pre[x]);
	dis[x]+=dis[pre[x]];
	return pre[x]=sum;
}
int main(){
	scanf("%d",&t);
	for (int i=1;i<=30000;++i){
		pre[i]=i,num[i]=1;
	}
	for (int i=1;i<=t;++i){
		cin>>f>>a>>b;
		x=find(a),y=find(b);
		if (f=='M'){
			dis[x]=num[y];//x点到了末尾
			num[y]+=num[x];//b排要加上a排原数
			num[x]=0;//a排已经并到b排,所以没有战舰
			pre[x]=y;//真正的合并
		}
		else{
			if (x!=y){
				puts("-1");
			}
			else{
				printf("%d\n",abs(dis[a]-dis[b])-1);
			}
		}
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值