银河英雄传说

题意:在并查集的基础上,还要求出同一集合的两个点的距离

这道题用并查集自己是知道的,但是竟然可以这么骚的操作。

下面转自大佬的查详细题解

初见这道题,首先想到的方法当然是直接模拟,模拟每一次指令。当然这种方法对于小数据行得通,但对于此题的500,000个指令,肯定超时。

因此我们就要想其它方法。

先来分析一下这些指令的特点,很容易发现对于每个M指令,只可能一次移动整个队列,并且是把两个队列首尾相接合并成一个队列,不会出现把一个队列分开的情况,因此,我们必须要找到一个可以一次操作合并两个队列的方法。

再来看下C指令:判断飞船i和飞船j是否在同一列,若在,则输出它们中间隔了多少艘飞船。我们先只看判断是否在同一列,由于每列一开始都只有一艘飞船,之后开始合并,结合刚刚分析过的M指令,很容易就想到要用并查集来实现。

定义一个数组fa,fa[i]表示飞船i的祖先节点,即其所在列的队头。再定义一个用于查找飞船祖先的函数find,在每次递归找祖先的同时更新fa,压缩路径,大大减小以后的时间消耗。初始时对于每个fa[i]都赋值为i,合并时就先分别查找飞船i和飞船j的祖先,然后将飞船i的祖先的祖先(即fa[飞船i的祖先])赋值为飞船j的祖先。最后每次判断时只需要找到飞船i和飞船j的祖先,判断是否是同一艘飞船,若是,则在同一列,反之,则不在。

现在,判断是否在同一列以及如何一次操作合并两个队列的问题已经解决,但还有问题需要解决:如何在以上方法的基础上,进一步得到两艘飞船之间的飞船数量呢?

我们先来分析一下:两艘飞船之间的飞船数量,其实就是艘飞船之间的距离,那么,这就转换为了一个求距离的问题。两艘飞船都是在队列里的,最简单的求距离的方法就是前后一个一个查找,但这个方法太低效,会超时。看见多次求两个点的距离的问题,便想到用前缀和来实现:开一个front数组,front[i]表示飞船i到其所在队列队头的距离,然后飞船i和飞船j之间的飞船数量即为它们到队头的距离之差减一,就是abs(front[i]-front[j])-1。

解决了如何高效得到两艘飞船之间飞船数量的问题,便又发现了新的问题:如何在之前方法的基础上,得到每艘飞船和队头的距离呢?

来分析一下现在已经使用的算法——并查集,它的特点:不是直接把一个队列里的所有飞船移到另一个队列后面,而是通过将要移动的队列的队头连接到另一个队列的队头上,从而间接连接两个队列。因此,我们在这个算法的基础上,每次只能更新一列中一艘飞船到队头的距离(如果更新多艘的话并查集就没有意义了)。

那么,该更新哪艘飞船呢?现在我们已经知道,使用并查集合并两个队列时只改变队头的祖先,而这个队列里其它飞船的祖先还是它原来的队头,并没有更新,所以这个队列里的其它飞船在队列合并之后,仍然可以找到它原来的队头,也就可以使用它原来队头的数据,因此,在每次合并的时候,只要更新合并前队头到目前队头的距离就可以了,之后其它的就可以利用它来算出自己到队头的距离。

理清了思路,但又有问题出现:该怎样更新呢?该怎么计算呢?

更新很容易,我们来分析一下:对于原来的队头,它到队头的距离为0,当将它所在的队列移到另一个队列后面时,它到队头的距离就是排在它前面的飞船数,也就是合并前另一个队列的飞船数量。因此,就知道该怎样实现了,我们再建一个数组num,num[i]表示以i为队头的队列的飞船数量,初始时都是1,在每次合并的时候,px为合并前飞船i的队头,py为合并前飞船j的队头,每次合并时,先更新front[px],即给它加上num[py],然后开始合并,即fa[px]=py,最后更新num, num[py]+= num[px];num[px]=0。

现在就差最后一步了:如何计算每个飞船到队头的距离。再来分析一下:对于任意一个飞船,我们都知道它的祖先(不一定是队头,但一定间接或直接指向队头),还知道距离它祖先的距离。对于每一个飞船,它到队头的距离,就等于它到它祖先的距离加上它祖先到队头的距离,而它的祖先到队头的距离,也可以变成类似的。可以递归实现,由于每一次更新都要用到已经更新完成的祖先到队头的距离,所以要先递归找到队头,然后在回溯的时候更新(front[i]+=front[fa[i]]),可以把这个过程和查找队头的函数放在一起。

下面是我的ac代码

#include <iostream>
#include <string>
#include <cstdio>
#include <algorithm>
const int maxn = +;
using namespace std; int front[maxn],num[maxn],fa[maxn];
string s; void init(){
for(int i=;i<maxn;i++)
{
fa[i] = i;
num[i] =; //表示这个团体的个数
front[i] = ; //表示和头节点的距离
}
}
int find(int x)
{
if(fa[x]==x)return x;
int fn = find(fa[x]);
front[x] += front[fa[x]]; //在回溯的时候更新front(因为更新时要用到正确的front[祖先],所以只能在回溯的时候更新)
return fa[x] = fn;
}
void uni(int x,int y)
{
int px = find (x);
int py = find (y);
if(px==py)return;
else
{
fa[px] = py; //将py设为px的祖先
front[px] += num[py]; //更新front[x所在列队头(现在在y所在队列后面)]即加上y所在队列的长度
num[py] += num[px]; //更新以py为队头队列的长度
num[px] = ; //以px为队头的队列已不存在,更新
}
}
int main(){
int T;
scanf("%d",&T);
init();
while(T--)
{
int u,v;
cin>>s>>u>>v;
if(s[]=='M')
{
uni(u,v);
}
else
{
int px = find (u);
int py = find (v);
if(px != py)printf("-1\n");
else
{
int tmp = front[u] - front[v]; //
if(tmp<)tmp=-tmp; //取两个点的绝对值再减去1;
printf("%d\n",tmp-); //
}
}
}
return ;
}

洛谷P1196[NOI2002]银河英雄传说-并查集扩展的更多相关文章

  1. 边带权并查集 学习笔记 & 洛谷P1196 [NOI2002] 银河英雄传说 题解

    花了2h总算把边带权并查集整明白了qaq 1.边带权并查集的用途 众所周知,并查集擅长维护与可传递关系有关的信息.然而我们有时会发现并查集所维护的信息不够用,这时"边带权并查集"就 ...

  2. [洛谷P1196][NOI2002]银河英雄传说 - 带偏移量的并查集(1)

    Description 公元五八〇一年,地球居民迁至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发 ...

  3. 洛谷P1196 [NOI2002]银河英雄传说(带权并查集)

    题目描述 公元五八○一年,地球居民迁至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山压顶 ...

  4. Luogu P1196 [NOI2002]银河英雄传说 | 并查集

    题目链接 并查集,具体看注释. #include<iostream> #include<cstdio> #include<cmath> using namespac ...

  5. 洛谷——P1196 [NOI2002]银河英雄传说

    P1196 [NOI2002]银河英雄传说 题目大意: 给你一个序列,支持两种操作: 合并指令为$M_{i,j}$j​,含义为第i号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第j号战舰所 ...

  6. 洛谷 1196 [NOI2002]银河英雄传说【模板】带权并查集

    [题解] 经典的带权并查集题目. 设cnt[i]表示i前面的点的数量,siz[i]表示第i个点(这个点是代表元)所处的联通块的大小:合并的时候更新siz.旧的代表元的cnt,路径压缩的时候维护cnt即 ...

  7. 洛谷P1196 [NOI2002] 银河英雄传说

    #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #includ ...

  8. 洛谷 P1196 [NOI2002]银河英雄传说

    题意简述 有30000列,每列都有一艘战舰,编号1~30000 有2种操作: 1.将一列的战舰运到另一列 2.询问两个战舰是否在同一列,如果是,求出它们之间的距离 题解思路 并查集, 维护每个点x离自 ...

  9. 洛谷 P1196 【银河英雄传说】

    这道题其实就是一个带权并查集的基础题,维护的是点权,所以我们要维护两个数组dis:表示当前点到父亲节点的距离,size:当前子树的大小.那么程序就自然出来了: 代码: #include <bit ...

随机推荐

  1. druid0.15.0安装方式

    Druid0.15.0安装文档 1 集群规划 Master包含Coordinator和Overlord,4核16G*2: data包含Historical和MiddleManager,16核64G*3 ...

  2. 关于python中的特殊方法

    研究了几个小时,大概对python中的特殊方法一知半解,现在写写自己的理解,以及记录一些找到的资源.待自己有比较深入理解的时候,再来更新 https://docs.python.org/3/refer ...

  3. 代码生成java连接数据库的所需代码(超详细)

    开始学习: round 1:(一开始学习当然还是要一步一步学习的啦,哪有什么一步登天!!!) a.准备工作:1.eclipse,mysql(这两个软件肯定要的啦,不然学什么把它们连接起来) 2.加载驱 ...

  4. Netty源码解析---服务端启动

    Netty源码解析---服务端启动 一个简单的服务端代码: public class SimpleServer { public static void main(String[] args) { N ...

  5. springboot管理类,springboot注入类

    springboot管理类,springboot注入类 定义一个配置类,添加@Configuration注解,EvaluatorTemplate代表你需要注入的第三方类 @Configuration ...

  6. [Chat]实战:仿网易云课堂微信小程序开发核心技术剖析和经验分享

    本Chat以一个我参与开发并已上线运营近2年——类似网易云课堂的微信小程序项目,来进行微信小程序高级开发的学习. 本场Chat围绕项目开发核心技术分析,帮助你快速掌握在线视频.音频类小程序开发所需要的 ...

  7. SmartSql使用教程(4)——多库配置与使用

    一.引言 已经几个月没更新了.本来上一章的预告是准备写TypeHandler的相关特性的.但是在准备的时候.SmartSql的作者重构了一下TypeHandler,使得我一下子没搞懂TypeHandl ...

  8. 【JDK】JDK源码分析-CyclicBarrier

    概述 CyclicBarrier 是并发包中的一个工具类,它的典型应用场景为:几个线程执行完任务后,执行另一个线程(回调函数,可选),然后继续下一轮,如此往复. 打个通俗的比方,可以把 CyclicB ...

  9. WebGL简易教程(二):向着色器传输数据

    目录 1. 概述 2. 示例:绘制一个点(改进版) 1) attribute变量 2) uniform变量 3) varying变量 3. 结果 4. 参考 1. 概述 在上一篇教程<WebGL ...

  10. 剑指offer-链表

    1. 链表中环的入口节点 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null. 思路一:用哈希表存已经遍历过的节点,O(1)复杂度查找,如果再次遇到就是环入口 # -*- cod ...