题意:对于一个使用十六进制读入的 \(01\) 矩阵,求其中 \(1\) 的连通块个数,空间限制 16MB 。\(n\le 2^{12},m\le2^{14}\)

我们认为如何读入是比较基础的内容,不作过多的介绍,具体请看代码。

离线下来怎么做

首先,如果不考虑空间限制,这题是比较简单的。我们可以直接在图上 DFS 获得 \(O(nm)\) 的做法。但是空间限制要求我们不能把全图存下来。我们就只能一行一行读入,考虑在线的做法。

在线,但不考虑空间限制

我们每次读入一行,考虑会发生什么。我们发现 DFS的本质就是 染色 。我们保存上一行的状态和颜色,读入当前一行的状态。

然后从当前行往上看。如果上一行的当前位置是 \(1\),那么它一定被涂色过了,给当前格子涂上它的颜色。从它开始往左右两边循环涂色(在这一层扩张连通块),如果遇到了 \(0\) 就结束, 如果遇到了别的颜色,那么就意味着当前的颜色和另外的颜色会合并起来。

最后,有些格子可能不能从上一行继承颜色,就需要新建颜色给它们。

先不考虑空间限制,考虑如何在线维护。

我们可以给所有的颜色开一个并查集,每次在并查集上维护两种颜色是否相同,合并两种颜色。最后直接扫描整个并查集,看其中有多少个等价类。

注意实现细节,为了每个点都只被扫到一次,我们 优先用左侧的颜色覆盖右边的 ,当覆盖到一个可以从上方继承别的颜色的格子的时候,进行颜色合并。

但是颜色的数量可能达到 \(O(nm)\) 的级别,不能直接开并查集,考虑优化。

在线且考虑空间复杂度

我们发现,执行到当前行, 有效的颜色(可能对下面产生影响的)只有前一行的所有颜色,总数是 \(O(m)\) 的级别。 我们可以每次重新给颜色编号,例如,当前前一行的颜色是 1 2 5 6 7 ,我们直接将其改成 1 2 3 4 5 ,后面新增颜色的时候就从 6 开始。

如果有相同颜色呢?用 map 吗?带 \(\log\) 基本就挂了。我们发现,每行增加的颜色数量也是 \(O(m)\) 的,所以颜色的值域是 \(O(m)\) 的。我们就可以直接用长度 \(2m\) 的数组暴力维护原先值域的所有颜色到新值域的映射,\(O(1)\) 检查 当前值映射是否存在,从而决定是否建立新的映射。

因此,我们就可以只开 \(O(m)\) 的并查集数组而非 \(O(nm)\) 的。注意并查集每次都要初始化(因为我们是在 滚动地处理问题 )。并且,因为每次的并查集不往下继承,即使上一层属于同一并查集的结果,下面也会认为他们是不同的,所以 每次运算结束之后要进行推平。

具体而言,当前在并查集上, 12 是同一集合内的。但是如果到了下面一层,因为当前的信息丢失,下一层就会认为 12 是不同颜色。因此,我们最后把所有的 color[i] 更改成 find(color[i]) ,就可以保证下一次不会把同样的颜色辨认成不同的。 (相当于路径压缩)

如何计算答案呢?我们发现,每当我们分配一个颜色,就会增加一个连通块。 合并两种颜色,其中一个颜色就被删除,减去一个连通块。 可以直接在合并的同时计算。

时间复杂度优化

而直接实现的做法会带一个并查集的 \(\log\),会挂。

众所周知,如果我们同时在并查集上使用启发式合并和路径压缩,就可以把并查集的复杂度优化到 \(O(\alpha(n))\) 。而 \(\alpha(m)\) 是很小的,能有 \(4\) 就顶了天了。于是,我们就得到了一个 \(O(nm\alpha(m))\) 的做法,空间是 \(O(m)\) 的,足以通过这道题。

代码

#define rd(i,n) for(int i=0;i<n;i++)
#define rp(i,n) for(int i=1;i<=n;i++)
#define st string
#define pb push_back
typedef long long ll;
int n,m;
int a[20005],b[20005],cnt,cb[20005],c[20005],ans,ers[20005];
int fa[40005],sz[40005],app[40005],lstcnt;
inline void getlne(){
st s;
cin>>s;
//滚动
rp(i,m)b[i]=a[i],cb[i]=c[i],c[i]=0;
//读入
rd(i,m/4){
int val=0;
if(s[i]>='A')val=10+s[i]-'A';
else val=s[i]-'0';
a[4*i+1]=(val>>3&1);
a[4*i+2]=(val>>2&1);
a[4*i+3]=(val>>1&1);
a[4*i+4]=(val>>0&1);
}
//建立旧的值域到新的值域的映射
lstcnt=cnt,cnt=0;
rp(i,m)if(cb[i]){
if(app[cb[i]]){
cb[i]=app[cb[i]];
}else {
int cur=++cnt;
app[cb[i]]=cur;
cb[i]=cur;
}
}
//记得清空映射,初始化并查集
rp(i,lstcnt)if(app[i])app[i]=0;
rp(i,cnt)fa[i]=i,sz[i]=1;
}
inline void merge(int x,int y){
if(x==y)return;
else ans--;//在这里维护答案
if(sz[x]>sz[y])sz[x]+=sz[y],fa[y]=x;
else sz[y]+=sz[x],fa[x]=y;
}
inline int head(int x){
return fa[x]==x?x:fa[x]=head(fa[x]);
}
inline void solve(){
//先考虑有颜色继承的往左右覆盖
rp(i,m)if(a[i]&&b[i]&&!c[i]){
int l=i,r=i;c[i]=cb[i];
while(l>1&&a[l-1])l--,c[l]=c[i];
while(r<m&&a[r+1]){
r++;
//合并颜色
if(cb[r]&&cb[r]!=c[i])merge(head(cb[r]),head(c[i]));
c[r]=c[i];
}
i=r;
}
//颜色推平
rp(i,m)if(c[i])c[i]=head(c[i]);
//新分配颜色
rp(i,m)if(a[i]&&!c[i]){
c[i]=++cnt;ans++;
while(i<m&&a[i+1])i++,c[i]=c[i-1];
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
rp(i,n){
getlne();
solve();
}
cout<<ans<<endl;
return 0;
}
//Crayan_r

CF884E - Binary Matrix的更多相关文章

  1. A.Kaw矩阵代数初步学习笔记 3. Binary Matrix Operations

    “矩阵代数初步”(Introduction to MATRIX ALGEBRA)课程由Prof. A.K.Kaw(University of South Florida)设计并讲授. PDF格式学习笔 ...

  2. Educational Codeforces Round 20 A. Maximal Binary Matrix

    A. Maximal Binary Matrix time limit per test 1 second memory limit per test 256 megabytes input stan ...

  3. 【leetcode】1284. Minimum Number of Flips to Convert Binary Matrix to Zero Matrix

    题目如下: Given a m x n binary matrix mat. In one step, you can choose one cell and flip it and all the ...

  4. 【leetcode】1253. Reconstruct a 2-Row Binary Matrix

    题目如下: Given the following details of a matrix with n columns and 2 rows : The matrix is a binary mat ...

  5. LeetCode 1284. Minimum Number of Flips to Convert Binary Matrix to Zero Matrix (最少翻转次数将二进制矩阵全部置为0)

    给一个矩阵mat,每个格子都是0或1,翻转一个格子会将该格子以及相邻的格子(有共同边)全部翻转(0变为1,1变为0) 求问最少需要翻转几次将所有格子全部置为0. 这题的重点是数据范围,比赛结束看了眼数 ...

  6. uva12534 Binary Matrix 2(最小费用最大流)

    http://blog.csdn.net/qq564690377/article/details/17082055 做的时候觉得明显是费用流,但是真的不知道怎么建图,看了上面的博客会稍微清晰一点.后面 ...

  7. Maximal Binary Matrix CodeForces - 803A (贪心+实现)

    题目链接 题意有点坑: 给你一个N*N的矩阵,让你填入K个1,使之整个矩阵关于左上到右下的对角线对称,并且这个要求这个矩阵的字典序最大. 对矩阵的字典序的定义是从每一行的第一个元素开始比较,大着为字典 ...

  8. CodeForces 803A Maximal Binary Matrix

    枚举. 枚举对角线上放多少个$1$,剩余的贪心放,更新答案. #include <iostream> #include <cstdio> #include <cstrin ...

  9. LeetCode 1091. Shortest Path in Binary Matrix

    原题链接在这里:https://leetcode.com/problems/shortest-path-in-binary-matrix/ 题目: In an N by N square grid, ...

  10. Codeforces 884E E. Binary Matrix

    题 OvO http://codeforces.com/contest/884/problem/E 884e 解 考虑并查集,每个点向上方和左方的点合并,答案即为1的总数减去需要合并的次数 由于只有1 ...

随机推荐

  1. 【基础语法规范】BC1:Hello Nowcoder

    语言1:Java public class Main{ public static void main(String[] args){ System.out.println("Hello N ...

  2. 【面试真题】ThoughtWorks-编程结对技术面试(一面)-2022年2月11日

    一.技术问题 1.Hbase (1)介绍 (2)项目中是否有用到 于:存大量数据(千万),考虑性能,方便进行数据处理,对其进行分析 自己:ADS层和Flink的数据,DWD计算出的的中间层数据存入DW ...

  3. 从零入门项目集成Karate和Jacoco,配置测试代码覆盖率

    解决问题 在SpringBoot项目中,如何集成Karate测试框架和Jacoco插件.以及编写了feature测试文件,怎么样配置才能看到被测试接口代码的覆盖率. 演示版本及说明 本次讲解,基于Sp ...

  4. Pytorch框架详解之一

    Pytorch基础操作 numpy基础操作 定义数组(一维与多维) 寻找最大值 维度上升与维度下降 数组计算 矩阵reshape 矩阵维度转换 代码实现 import numpy as np a = ...

  5. 手写promise解决回调地狱问题

    在介绍promise之前我们先来看一段代码: 根据案例我们可以看出,这段代码可以无限的嵌套下去,但是每嵌套一层,代码的运行就会降低,而解决回调地狱最好的办法就是new promise 一.什么是 pr ...

  6. 04-CURD

    Insert @Test//测试插入 public void insertTest(){ User user = new User(); user.setName("wsk"); ...

  7. CTFshow——funnyrsa1的wp理解

    题目如下: 题目分析: 拿到题,发现给的e不常规,p1和p2相等,有两个不同n,两个不同c和两个不同e.给定两个密文的情况下,通常需要找到两者之间存在的关系,"合并"密文求解才能得 ...

  8. TypeScript 前端工程最佳实践

    作者:王春雨 前言 随着前端工程化的快速发展, TypeScript 变得越来越受欢迎,它已经成为前端开发人员必备技能. TypeScript 最初是由微软开发并开源的一种编程语言,自2012年10月 ...

  9. 迁移学习(DANN)《Domain-Adversarial Training of Neural Networks》

    论文信息 论文标题:Domain-Adversarial Training of Neural Networks论文作者:Yaroslav Ganin, Evgeniya Ustinova, Hana ...

  10. @Transactional事务回滚异常:Transaction rolled back because it has been marked as rollback-only

    问题描述 事务设置手动回滚:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() 代码需要返回比较友好的提示,但t ...