最近带权并查集这块比较薄弱,直接看食物链看不懂,就老实一步步来了。


有一个划分为N列的星际战场,各列依次编号为1,2,…,N。

有N艘战舰,也依次编号为1,2,…,N,其中第i号战舰处于第i列。

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

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

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

现在需要你编写一个程序,处理一系列的指令。

输入格式

第一行包含整数T,表示共有T条指令。

接下来T行,每行一个指令,指令有两种形式:M i j或C i j。

其中M和C为大写字母表示指令类型,i和j为整数,表示指令涉及的战舰编号。

输出格式

你的程序应当依次对输入的每一条指令进行分析和处理:

如果是M i j形式,则表示舰队排列发生了变化,你的程序要注意到这一点,但是不要输出任何信息;

如果是C i j形式,你的程序要输出一行,仅包含一个整数,表示在同一列上,第i号战舰与第j号战舰之间布置的战舰数目,如果第i号战舰与第j号战舰当前不在同一列上,则输出-1。

数据范围

N≤30000,T≤500000

原题链接:https://www.acwing.com/problem/content/240/


由于直接维护每对战舰之间的距离的时间复杂度非常大,所以我们考虑维护每个战舰到队头的距离,这样如果两只战舰在同一列上且i != j,那么我们就直接通过两个战舰各自到队头的距离相减得到答案,所以我们的查询函数可以这么写:

int Query(int a, int b)
{
if(a == b) return ;
if(issame(a, b)) return abs(d[a] - d[b]) - ; return -;
}

既然查询总是涉及子节点跟祖宗节点产生直接联系,那么我们可以考虑使用带权并查集来维护这个d[i],这个d[i]代表自己到父节点的距离,但是由于并查集的路径压缩,最终每个子节点的父节点都会变成他们的祖宗,也就是说这个d[i]就变成了子节点到祖宗的距离。

我们知道,对于祖宗来说,它到自己的距离是0,所以祖宗的d[i]是0,同时我们也能推出初始化的d[]数组也应该全部是0

那么在一棵树上的d[x]的更新跟路径压缩怎么去弄呢?

假设目前这颗树是长这样子的

那么路径压缩后应该长这样子

这里边发生的变化,其实就是3号节点接到了1号节点上,并且因为2号节点到1号节点的距离为1,然后3号节点到2号节点距离也是1,那么我们可以计算出3号节点到1号节点距离应该是d[2] + d[3] = 2。

达成这个目的的具体步骤也不难,就是先顺着3号节点用递归方式摸到2号节点再摸到1号节点,然后又从一号节点顺着那根线回来把距离都加到手

所以,我们的查找祖宗+路径压缩的代码就如下

int find(int x)
{
if(x == fa[x]) return x; //是祖宗就直接返回自己 int root = find(fa[x]); //找到根节点
d[x] += d[fa[x]]; //递归把距离一路加回来
fa[x] = root; //把子节点接到祖宗节点上
return root;
}

但是有个问题,两棵树合并时,也就是一个队插到另一个队的后面时,这个d怎么去更新才合适呢?

容易知道,当一个队插到另一个队的后面时,这个队的队头就排到了另一个队的队尾,那么我们就推算出这个队头到另一个队头的距离数值上等于另一个队的长度

因为涉及到队的长度,所以我们还要再引入一个信息,就是队的长度L

因为队的长度只有在两个不同的队合并时才会发生变化,所以这个L[]还是十分好维护的,就是两队长度相加即可

所以我们合并的时候,先更新祖宗d[x]再更新L[]就能达成任务了

void merge(int a, int b)
{
if(!issame(a, b)){
int root_a, root_b;
root_a = find(a);
root_b = find(b);
d[root_a] = l[root_b];
l[root_b] += l[root_a];
fa[root_a] = root_b;
}
}

查找、路径压缩以及合并,并查集的这些功能就水到渠成了

之后用就行


贴上AC代码

#include<iostream>
#include<algorithm>
using namespace std; const int Maxn = 3e4 + ;
int fa[Maxn];
int l[Maxn];
int d[Maxn]; int find(int x)
{
if(x == fa[x]) return x; //是祖宗就直接返回自己 int root = find(fa[x]); //找到根节点
d[x] += d[fa[x]]; //递归把距离一路加回来
fa[x] = root; //把子节点接到祖宗节点上
return root;
} bool issame(int a, int b)
{
return find(a) == find(b);
} void merge(int a, int b)
{
if(!issame(a, b)){
int root_a, root_b;
root_a = find(a);
root_b = find(b);
d[root_a] = l[root_b];
l[root_b] += l[root_a];
fa[root_a] = root_b;
}
} int Query(int a, int b)
{
if(a == b) return ;
if(issame(a, b)) return abs(d[a] - d[b]) - ; return -;
} int main()
{
for(int i=; i<Maxn ;i++){
fa[i] = i;
l[i] = ;
} int m;
cin>>m;
while(m--){
char op;
int i, j;
cin>>op>>i>>j;
if(op == 'M') merge(i, j);
else cout<<Query(i, j)<<endl;
} return ;
}

ACWing 238 银河英雄传说的更多相关文章

  1. NOI2002_ Galaxy银河英雄传说86

    NOI2002_ Galaxy银河英雄传说86     公元五八○一年,地球居民迁移至金牛座α第二行星,:宇宙历七九九年,银河系的两大军事集团在巴米利恩星:杨威利擅长排兵布阵,巧妙运用各种战术屡次以少 ...

  2. P4847 银河英雄传说V2 题解(Splay)

    题目链接 P4847 银河英雄传说V2 解题思路 我天哪!!!\(splay\)在\(rotate\)的时候先\(upd(y)\)再\(upd(x)\)!!以后不能再因为这个\(WA\)一晚上了!!! ...

  3. codevs1540 银河英雄传说

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

  4. NOI2002 洛谷 P1196 银河英雄传说

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

  5. NOI2002 银河英雄传说

    P1196 银河英雄传说 367通过 1.1K提交 题目提供者该用户不存在 标签并查集NOI系列2001(或之前) 难度提高+/省选- 提交该题 讨论 题解 记录 最新讨论 莱因哈特什么鬼? 私人代码 ...

  6. codevs 1540 银河英雄传说

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

  7. 数据结构(并查集):COGS 260. [NOI2002] 银河英雄传说

    260. [NOI2002] 银河英雄传说 ★★☆   输入文件:galaxy.in   输出文件:galaxy.out   简单对比时间限制:5 s   内存限制:128 MB [问题描述] 公元五 ...

  8. CH4101 银河英雄传说

    题意 4101 银河英雄传说 0x40「数据结构进阶」例题 描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展.  宇宙历七 ...

  9. codevs 1540 1540 银河英雄传说

    1540 银河英雄传说 题目描述 Description 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银 ...

随机推荐

  1. .NET CORE(C#) WPF 抽屉式菜单

    微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. .NET CORE(C#) WPF 抽屉式菜单 阅读导航 本文背景 代码实现 本文参考 源码 ...

  2. SpringBoot整合NoSql--(三)Redis集群

    (1)集群原理 在Redis集群中,所有的Redis节点彼此互联,节点内部使用二进制协议优化传输速度和带宽. 当一个节点挂掉后,集群中超过半数的节点检测失效时才认为该节点已失效.不同于Tomcat集群 ...

  3. PTA 凑零钱(深度优先搜索)

    韩梅梅喜欢满宇宙到处逛街.现在她逛到了一家火星店里,发现这家店有个特别的规矩:你可以用任何星球的硬币付钱,但是绝不找零,当然也不能欠债.韩梅梅手边有 10000 枚来自各个星球的硬币,需要请你帮她盘算 ...

  4. js模拟form提交 导出数据

    //创建模拟提交formfunction dataExport(option) { var form = $("<form method='get'></form>& ...

  5. 【终端使用】"scp"命令,远程拷贝文件

    一."scp"命令的使用 "scp"命令,是"secure copy (remote file copy program)"英文单词的缩写, ...

  6. 在IE8的基础上安装IE11

    安装包下载 链接:https://pan.baidu.com/s/1WAuyMg_SrfVa0wLjoop5Jw   提取码:nh1r 安装方式 先运行 - EIE11_ZH-CN_WOL_WIN76 ...

  7. 前端页面中:jsp和HTML的区别之处

    JSP和HTML的区别 HTML页面是静态页面,也就是事先由用户写好放在服务器上,固定内容,不会变,由web服务器向客户端发送,平时上网看的网页都是大部分都是基于html语言的. JSP页面是有JSP ...

  8. C++&C语言 -> //有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛?

    /*    a                  b                          c       d 1  5                   5               ...

  9. Spark学习之路 (一)Spark初识 [转]

    官网介绍 什么是Spark 官网地址:http://spark.apache.org/ Apache Spark™是用于大规模数据处理的统一分析引擎. 从右侧最后一条新闻看,Spark也用于AI人工智 ...

  10. 852. spfa判断负环

    给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数. 请你判断图中是否存在负权回路. 输入格式 第一行包含整数n和m. 接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点 ...