洛谷P1196[NOI2002]银河英雄传说-并查集扩展
银河英雄传说
题意:在并查集的基础上,还要求出同一集合的两个点的距离
这道题用并查集自己是知道的,但是竟然可以这么骚的操作。
下面转自大佬的查详细题解
初见这道题,首先想到的方法当然是直接模拟,模拟每一次指令。当然这种方法对于小数据行得通,但对于此题的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]银河英雄传说-并查集扩展的更多相关文章
- 边带权并查集 学习笔记 & 洛谷P1196 [NOI2002] 银河英雄传说 题解
花了2h总算把边带权并查集整明白了qaq 1.边带权并查集的用途 众所周知,并查集擅长维护与可传递关系有关的信息.然而我们有时会发现并查集所维护的信息不够用,这时"边带权并查集"就 ...
- [洛谷P1196][NOI2002]银河英雄传说 - 带偏移量的并查集(1)
Description 公元五八〇一年,地球居民迁至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发 ...
- 洛谷P1196 [NOI2002]银河英雄传说(带权并查集)
题目描述 公元五八○一年,地球居民迁至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山压顶 ...
- Luogu P1196 [NOI2002]银河英雄传说 | 并查集
题目链接 并查集,具体看注释. #include<iostream> #include<cstdio> #include<cmath> using namespac ...
- 洛谷——P1196 [NOI2002]银河英雄传说
P1196 [NOI2002]银河英雄传说 题目大意: 给你一个序列,支持两种操作: 合并指令为$M_{i,j}$j,含义为第i号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第j号战舰所 ...
- 洛谷 1196 [NOI2002]银河英雄传说【模板】带权并查集
[题解] 经典的带权并查集题目. 设cnt[i]表示i前面的点的数量,siz[i]表示第i个点(这个点是代表元)所处的联通块的大小:合并的时候更新siz.旧的代表元的cnt,路径压缩的时候维护cnt即 ...
- 洛谷P1196 [NOI2002] 银河英雄传说
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #includ ...
- 洛谷 P1196 [NOI2002]银河英雄传说
题意简述 有30000列,每列都有一艘战舰,编号1~30000 有2种操作: 1.将一列的战舰运到另一列 2.询问两个战舰是否在同一列,如果是,求出它们之间的距离 题解思路 并查集, 维护每个点x离自 ...
- 洛谷 P1196 【银河英雄传说】
这道题其实就是一个带权并查集的基础题,维护的是点权,所以我们要维护两个数组dis:表示当前点到父亲节点的距离,size:当前子树的大小.那么程序就自然出来了: 代码: #include <bit ...
随机推荐
- cmd与monkey测试
monkey测试的相关命令 monkey是模拟用户触摸操作,不支持条件判断.monkey命令格式: 启动安卓模拟器/真机 点击运行->输入cmd->进入命令行界面 查看设备连接情况,ad ...
- WTM 构建DotNetCore开源生态,坐而论道不如起而行之
作为一个8岁开始学习编程,至今40岁的老程序员,这辈子使用过无数种语言,从basic开始,到pascal, C, C++,到后来的 java, c#,perl,php,再到现在流行的python. 小 ...
- Docker 的另外两个话题: DockerHub 和 容器网络
关注公众号,大家可以在公众号后台回复“博客园”,免费获得作者 Java 知识体系/面试必看资料. Hello,大家好,我是子悠,作为本周的小编昨天已经跟大家介绍过自己了,今天就让我们继续学习江南一点雨 ...
- Android buildType混淆代码
[话题引入] ①在Android开发完成,我们会将代码打包成APK文件.选择 菜单栏 Build --> Build APK ②将查看视图切换到 Project 模式,文件夹下有一个debug模 ...
- centos开发环境安装
执行 yum install gcc gcc-c++ gcc-g77 flex bison autoconf automake bzip2-devel zlib-devel ncurses-devel ...
- Spring Boot 修改静态资源一定要重启项目才会生效吗?未必!
回顾热部署 Spring Boot 中的热部署相信大家都用过吧,只需要添加 spring-boot-devtools 依赖就可以轻松实现热部署.Spring Boot 中热部署最最关键的原理就是两个不 ...
- Spark 系列(四)—— RDD常用算子详解
一.Transformation spark 常用的 Transformation 算子如下表: Transformation 算子 Meaning(含义) map(func) 对原 RDD 中每个元 ...
- css常用代码块
顶部固定导航栏 | css position: fixed; top: 0; left: 0; z-index: 9999; width: 100%; height: 48px; border-top ...
- 反向传播 Backpropagation
前向计算:没啥好说的,一层一层套着算就完事了 y = f( ... f( Wlayer2T f( Wlayer1Tx ) ) ) 反向求导:链式法则 单独看一个神经元的计算,z (就是logit)对 ...
- django报错信息解决方法
You have 17 unapplied migration(s). Your project may not work properly until you apply the migration ...