银河英雄传说

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

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

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

初见这道题,首先想到的方法当然是直接模拟,模拟每一次指令。当然这种方法对于小数据行得通,但对于此题的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. [系列] Go - chan 通道

    目录 概述 声明 chan 写入 chan 读取 chan 关闭 chan 示例 推荐阅读 概述 原来分享基础语法的时候,还未分享过 chan 通道,这次把它补上. chan 可以理解为队列,遵循先进 ...

  2. python 处理json数据

    python 处理 json数据 以下是登录账号后获取的json数据,headers中注意加入cookie值 需要处理的数据如下: 全部代码如下 #!/usr/bin/env python # -*- ...

  3. 使用eclipse编写和运行java程序(基础)

    1.首先java程序的运行你需要下载和安装JDK,这是java运行的必备环境. 2.在桌面上找到eclipes,双击打开. 3.在eclipes启动的过程中,会弹出一个窗口,让你填写java工作区的保 ...

  4. Linux 清理空间

    背景: 在使用Linux服务器的时候,经常会碰到服务器上的磁盘空间满了,在该种情况下,必须进行磁盘空间清理. 解决方法: 示例:需要/tmp下空出至少1G的可用空间 分别执行的命令如下: df /tm ...

  5. k8s+istio:流量控制之灰度发布

    通过Kubernetes+Istio的流量控制实现灰度发布,主要演示通过流量权重实现蓝绿,通过http自定义头实现金丝雀 准备环境 k8s和istio不想自己装的话可以在云上买个按量付费集群,用完即删 ...

  6. 使用 RxJava 进行嵌套串行网络请求的一种方法

    需求 有这样一个列表数据,它包含了商店+订单的信息,获取订单列表时,订单实体中会包含商店的 ID,而列表显示时需要商店的名称和 logo,这时候就需要进行嵌套串行网络请求了. 关键词 flatMap ...

  7. python编写环境(种类)

    python编写环境(种类) 官方推荐 cpython 转成C的字节码 jython转成Java的字节码 ironpython转成C#字节码 pypy转换成动态编译 开发快,运行快

  8. rwcheck:为嵌入式设备设计的读写压测工具

    我设计的一款读写压测工具,开源在我的github仓库 rwcheck是一个对嵌入式设备进行读写压测的工具 什么是rwcheck 正如其名,rwcheck工具用于读写压测.它是什么工作原理呢?为什么要用 ...

  9. springmvc集成swaggerui

    这里先写下需要的pom.xml配置(我引用的2.4.0,相对稳定) 在集成springfox-swagger2之前,我也尝试着集成了swagger-springmvc,方式差不多,但是swagger- ...

  10. OpenXML性能真的低下吗?

    博文NET导出Excel的四种方法及评测 中对比了4个库的导出性能,但对OpenXML的评价并不高,本人觉得不合理,所以我重新测试下性能 基于OpenXML的包装类 ExcelDownWorker p ...