题目链接

大致题意

相邻格子不同为连通,计算每个点所在的连通块大小。

想法

我采用了并查集的做法。

开一个辅助数组记录连通块大小,每次合并的时候更新父亲节点的大小即可。

一个点先与它上面的点判定,若判定连通则加入上方点所属的块中。

再与左边的点判定,若连通则再将两个块合并。

总体复杂度O(n3)O(n^3)O(n3),其中并查集不加优化复杂度为O(n)O(n)O(n),使用路径压缩+按节点大小合并优化并查集,将并查集复杂度降低为近似O(1)O(1)O(1),总体复杂度近似为O(n2)O(n^2)O(n2)。

并查集优化

路径压缩

每次查找的时候,将路径上的所有儿子的父亲改写为最原始的祖宗。这样下次就可以直接找到祖宗。

实现:

inline int find(const int &x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);//路径压缩
}

通俗写法:

inline int find(const int &x)
{
if(fa[x] == x)//找到无父亲的节点,即为祖宗
return x;
fa[x] = find(fa[x]);
return fa[x];
}

按秩合并

思考:

有2个块需要合并,那么将小块接在大块的后面能够让整体复杂度更优。

可以将每个祖宗及其所有儿子看做一棵树,那么节点到根的距离就是查找父亲需要的次数。

将小树并入大树,可以避免树的深度过深。

这个rnk数组保存的是树的深度的上界。

通常写法:

const int maxn = 10000000;
int rnk[maxn],fa[maxn];//rnk为该节点所在子树的深度
inline void unite(const int &x,const int &y)
{
//合并x与y的块
int t1 = find(x),t2 = find(y);//找到各自的祖宗
if(t1 == t2)
return;
if(rnk[t1] == rnk[t2])
{
//此时两树深度相等,随便合并
fa[t1] = t2;
++rnk[t2];//合并后深度增加
}
else if(rnk[t1] > rnk[t2])
fa[t2] = t1;
else
fa[t1] = t2;
}

总结

路径压缩和按秩合并都能将并查集复杂度降为O(logn)O(logn)O(logn),而两者一起使用能够降为O(log∗n)O(log^*n)O(log∗n)。

nnn log∗nlog^*nlog∗n
(−∞, 1] 0
(1, 2] 1
(2, 4] 2
(4, 16] 3
(16, 65536] 4
(65536, 2^65536] 5

(数据转自链接,个人认为这篇博文讲并查集讲得相当不错。)

代码

因为按秩合并需要额外数组,而在此数量级下开数组的消耗可能比优化更大……

于是借用了题目中维护的“连通块大小”,即节点个数,进行了按size合并的优化。

#include <cstdio>
using namespace std;
#define getId(x, y) (((x - 1) * n) + y) //将二维的点赋予一个一维的别名
int fa[1001000];
int h[1001000];
char all[2][1010];
int n, m, i, j;
//读入优化
inline char nc()
{
static char buf[400000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 400000, stdin), p1 == p2) ? EOF : *p1++;
}
void read(char *s)
{
static char c;
for (c = nc(); c != '1' && c != '0'; c = nc());
for (; c == '0' || c == '1'; *++s = c, c = nc());
}
void read(int &r)
{
static char c; r = 0;
for (c = nc(); c > '9' || c < '0'; c = nc());
for (; c >= '0' && c <= '9'; r = (r << 1) + (r << 3) + (c ^ 48), c = nc());
}
//并查集
inline int find(const int &x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);//路径压缩
}
inline void unite(const int &a, const int &b)
{
int t1 = find(a), t2 = find(b);
if (t1 == t2)
return;
h[t1] > h[t2] ? fa[t2] = t1, h[t1] += h[t2] : fa[t1] = t2, h[t2] += h[t1]; //类似按秩合并的做法,合并同时将儿子的大小加入父亲的大小中
}
//
int main()
{
read(n);
read(m);
int n2 = n * n;
for (int i = 1; i <= n2; ++i)
fa[i] = i, h[i] = 1; //初始化每个点都是单独的联通块,大小为1 int now = 0, pre = 1;
//第一行特判,不需要与上一行作比较
i = 1;
read(all[now]);
for (j = 2; j <= n; ++j)//j从2开始,可以略过第一个格子,最左上角的格子无需判断
if (all[now][j] != all[now][j - 1])
unite(getId(i, j), getId(i, j - 1)); for (i = 2; i <= n; ++i)
{
now ^= 1;//使用滚动数组
pre ^= 1;
read(all[now]);
if (all[now][1] != all[pre][1])
unite(getId(i, 1), getId(i - 1, 1));//第一个格子无需与左边判断
for (j = 2; j <= n; ++j)
{
if (all[now][j] != all[now][j - 1]) //因为是按行按列遍历,实际上只需要处理每个点的上方点和左边点的合并
unite(getId(i, j), getId(i, j - 1));
if (all[now][j] != all[pre][j])
unite(getId(i, j), getId(i - 1, j));
}
}
int x, y;
for (i = 1; i <= m; ++i)
{
read(x);
read(y);
printf("%d\n", h[find(getId(x,y))]);
}
return 0;
}

[洛谷Luogu]P1141 01迷宫[联通块 并查集]的更多相关文章

  1. luogu P1141 01迷宫 x

    P1141 01迷宫 题目描述 有一个仅由数字0与1组成的n×n格迷宫.若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上. 你的任 ...

  2. luogu P1141 01迷宫

    https://www.luogu.org/problem/show?pid=1141 还不太会用 BFS 然后就跟着感觉走了一波 经历了很多错误 刚开始的读入 然后BFS的过程 最后T三个点 看到别 ...

  3. Luogu P1141 01迷宫【搜索/dfs】By cellur925

    题目传送门 我tm到现在还需要刷这种水搜索...我退役吧. 但就是搜索弱嘛 补一补嘛qwq 题目大意:给你一张地图与许多询问,每次询问求这个点所在联通块的点的个数. 所以这个题目的本质就是在求联通块. ...

  4. 洛谷P1991无线通讯网[kruskal | 二分答案 并查集]

    题目描述 国防部计划用无线网络连接若干个边防哨所.2 种不同的通讯技术用来搭建无线网络: 每个边防哨所都要配备无线电收发器:有一些哨所还可以增配卫星电话. 任意两个配备了一条卫星电话线路的哨所(两边都 ...

  5. 洛谷P4768 [NOI2018]归程(可持久化并查集,最短路)

    闲话 一个蒟蒻,在网络同步赛上进行了这样的表演-- T2组合计数不会,T3字符串数据结构不会,于是爆肝T1 一开始以为整个地图都有车,然后写了2h+的树套树,终于发现样例过不去 然后写可持久化并查集D ...

  6. 洛谷P3402 【模板】可持久化并查集 [主席树,并查集]

    题目传送门 可持久化并查集 n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 ...

  7. 洛谷 P2024 食物链 POJ 1182 Label:并查集Turbo

    题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种,但是我 ...

  8. 洛谷P3402 【模板】可持久化并查集(可持久化线段树,线段树)

    orz TPLY 巨佬,题解讲的挺好的. 这里重点梳理一下思路,做一个小小的补充吧. 写可持久化线段树,叶子节点维护每个位置的fa,利用每次只更新一个节点的特性,每次插入\(logN\)个节点,这一部 ...

  9. [洛谷P2024/POJ1182]食物链 - 带偏移量的并查集(2)

    Description 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的 ...

随机推荐

  1. 常用的HBase命令

    进入HBase shell:hbase shell 退出HBase shell:exit 查看HBase中所有的表:list 查看某个表中的记录总数:count 'table name' 查看某个表中 ...

  2. android使用友盟实现第三方登录、分享以及微信回调无反应问题解决办法

    这里介绍微信和新浪登录.微信登录和新浪登录都需要申请第三方账号.可以参考官方文档http://dev.umeng.com/social/android/operation#2还是很清晰的. 新浪微博开 ...

  3. R语言 使用rmarkdown写代码

    1.如果是第一次新建markdown文件,需要在有网的条件下,因为要下载一个包才能用markdown 2.为什么使用rmarkdown 使用markdown不仅可以边调试边运行,还可以一次性将所调试好 ...

  4. 「JLOI2014」聪明的燕姿

    传送门 Luogu 解题思路 很容易想到直接构造合法的数,但是这显然是会T飞的. 我们需要考虑这样一件事: 对于一个数 \(n\),对其进行质因数分解: \[n=\sum_{i=1}^x p_i^{c ...

  5. JDK8中的HashMap源码

    背景 很久以前看过源码,但是猛一看总感觉挺难的,很少看下去.当时总感觉是水平不到.工作中也遇到一些想看源码的地方,但是遇到写的复杂些的心里就打退堂鼓了. 最近在接手同事的代码时,有一些很长的pytho ...

  6. instance与可变参数合用,多态性

    public class Doubt { public static void main(String[] args) { Dog d1=new Dog(); Dog d2=new Zangao(); ...

  7. jdk环境变量、maven环境变量、Mysql环境变量配置

    jdk官网地址:http://www.oracle.com/index.htmlhttp://www.java.sun.com 一.配置 jdk环境变量1.新建JAVA_HOME,在变量值复制JDK安 ...

  8. Health Check【转】

    强大的自愈能力是 Kubernetes 这类容器编排引擎的一个重要特性.自愈的默认实现方式是自动重启发生故障的容器.除此之外,用户还可以利用 Liveness 和 Readiness 探测机制设置更精 ...

  9. 怎样管理Exchange Server 2013资源邮箱

    1. exchange资源邮箱介绍 这次将介绍Exchange Server 2013的资源邮箱相关内容. Exchange Server 2013的资源邮箱包含两类,其一为“会议室邮箱”,另一类是“ ...

  10. 变相降价的iPhone,能挽救苹果在中国的命运吗?

    人无千日好,花无百样红.当年iPhone的横空出世不仅开辟了智能手机时代,还间接导致了诺基亚.黑莓等手机品牌的没落.十余年来,苹果凭借iPhone活得风光无限,并成为全球首个市值超万亿美元的公司.但进 ...