Content

有一个 \(n\times m\) 的网格,网格上的格子被涂成了白色或者黑色。

设两个点 \((x_1,y_1)\) 和 \((x_2,y_2)\),如果以下三个条件均满足:

  • \(1\leqslant x_1<x_2\leqslant n\) 且 \(1\leqslant y_1<y_2\leqslant m\)。
  • \(2\mid (x_1+x_2)\)。
  • \((x_1,y_1)\rightarrow(x_2,y_1)\),\((x_1,y_2)\rightarrow(x_2,y_2)\),\((\dfrac{x_1+x_2}2,y_1)\rightarrow(\dfrac{x_1+x_2}2,y_2)\) 这三段中的格子均为白色。

那么我们称第三个条件中的三段构成的图形为 H 形。求 \(n\times m\) 的网格里面有多少个不同的 H 形。

数据范围:\(2\leqslant n,m\leqslant 2\times 10^3\)。

Solution

本题解分 Subtask 讲解。

Subtask 1 (1 pt):\(n=2\)

在这个 Subtask 中,我们无论如何选择两个点都不能同时满足前两个条件。因此答案是 \(0\)。

Subtask 2 (9 pts):\(n,m\leqslant 50\)

我们可以用 \(\mathcal O(n^2m^2)\) 枚举出两个点 \((x_1,y_1)\) 和 \((x_2,y_2)\),然后我们直接枚举三段并判断是否三段都是白色格子,最后如果三个条件都满足的话,将其统计入答案即可。

Subtask 3&4 (40 pts):\(n,m\leqslant 500\)。

我们发现我们直接枚举两个点的话太耗费时间了,能否有更快的方法?

我们为什么不枚举两个中间点呢?因为这两个点都在同一行,枚举的复杂度可以降至 \(\mathcal O(nm^2)\)。

但是如果再去枚举三段的话又太浪费时间了,能否有更快的方法?

这时候预处理就派上用场了。我们可以先预处理出点 \((i,j)\) 向上最多能够到达的连续 \(0\) 的个数 \(s_{i,j}\) 和向下最多能够到达的连续 \(0\) 的个数 \(d_{i,j}\)。可以通过枚举坐标再去向上向下直接暴力推,复杂度是 \(\mathcal O(n^2m)\) 的。

然后枚举中间点的时候就可以直接求出两个中间点 \((a,b)\)、\((c,d)\) 能够得到的 H 形的个数为 \(\min\{s_{a,b},d_{a,b},s_{c,d},d_{c,d}\}\)。直接统计入答案即可。

Subtask 5 (50 pts):正解

我们发现 \(n,m\leqslant 2\times 10^3\) 的时候,上面的算法的复杂度还是太大了。能否有更快的方法?

一开始想能否用线段树/树状数组处理,然而还是我太 naive 了没有想出来。后来想到了一个可以用栈来处理的方法。

我们直接扫一遍这个网格,每逢被涂成黑色的格子或者到了一行的尽头,就把这个栈里头的所有元素拿出来,按照从小到大的顺序排个序,然后每一个在栈里头的元素 \(st_k\),其对答案的贡献是 \(st_k\times (top-k)\)(其中 \(top\) 指原来栈里面的元素个数),把这个贡献加入里面去并清空整个栈。否则将 \(f_{i,j}=min\{s_{i,j},d_{i,j}\}\) 放入栈里头,复杂度大概是 \(\mathcal O(nm\log m)\) 的?

预处理也可以变成 \(\mathcal O(nm)\) 的,具体的就不多做赘述了,看代码吧。

Code

const int N = 2007;
int n, m, a[N][N], s[N][N], d[N][N], f[N][N]; namespace sub1 {
iv work() {return printf("0"), void();}
}
namespace sub2 {
iv work() {
int ans = 0;
F(int, x, 1, n) F(int, y, 1, m) Fo(int, x_, x + 2, n, 2) F(int, y_, y + 1, m) {
int fl = 1;
F(int, i, x, x_) if(a[i][y]) {fl = 0; break;}
F(int, i, x, x_) if(a[i][y_]) {fl = 0; break;}
F(int, j, y, y_) if(a[(x + x_) / 2][j]) {fl = 0; break;}
ans += fl;
}
return write(ans), void();
}
}
namespace sub3 {
iv work() {
int ans = 0;
F(int, i, 1, n) F(int, j, 1, m) if(!a[i][j]) F(int, k, j + 1, m) if(!a[i][k]) {
ans += min(min(min(s[i][j], d[i][j]), s[i][k]), d[i][k]);
} else break;
return write(ans), void();
}
}
namespace sub4 {
int st[N], top;
ll ans = 0;
iv work() {
F(int, i, 1, n) {
top = 0;
F(int, j, 1, m + 1) if(a[i][j] == 1 || j == m + 1) {
if(top) {
sort(st + 1, st + top + 1)/*, print_array1(st, top), puts("-------");*/;
F(int, k, 1, top - 1) ans += 1ll * st[k] * (top - k);
top = 0;
}
} else st[++top] = f[i][j];
}
return write(ans), void();
}
} int main() {
n = Rint, m = Rint;
F(int, i, 1, n) F(int, j, 1, m) a[i][j] = Rint;
// F(int, i, 1, n) F(int, j, 1, m) if(!a[i][j]) {
// F(int, k, i + 1, n) if(a[k][j] == 0) s[i][j]++; else break;
// R(int, k, i - 1, 1) if(a[k][j] == 0) d[i][j]++; else break;
// f[i][j] = min(s[i][j], d[i][j]);
// } //这是 O(n^2m) 的预处理
F(int, j, 1, m ) R(int, i, n, 1) s[i][j] = (a[i][j] == 0 ? ((a[i + 1][j] == 1 || i == n) ? 0 : s[i + 1][j] + 1) : 0);
F(int, j, 1, m) F(int, i, 1, n) d[i][j] = (a[i][j] == 0 ? ((a[i - 1][j] == 1 || i == 1) ? 0 : d[i - 1][j] + 1) : 0);
F(int, i, 1, n) F(int, j, 1, m) f[i][j] = min(s[i][j], d[i][j]); //这是 O(nm) 的预处理
if(n == 2) sub1 :: work();
else if(n <= 100 && m <= 100) sub2 :: work();
else if(n <= 500 && m <= 500) sub3 :: work();
else sub4 :: work();
return 0;
}

LuoguP7715 「EZEC-10」Shape 题解的更多相关文章

  1. 「GXOI / GZOI2019」简要题解

    「GXOI / GZOI2019」简要题解 LOJ#3083. 「GXOI / GZOI2019」与或和 https://loj.ac/problem/3083 题意:求一个矩阵的所有子矩阵的与和 和 ...

  2. loj#2054. 「TJOI / HEOI2016」树

    题目链接 loj#2054. 「TJOI / HEOI2016」树 题解 每次标记覆盖整棵字数,子树维护对于标记深度取max dfs序+线段树维护一下 代码 #include<cstdio> ...

  3. 【题解】#6622. 「THUPC 2019」找树 / findtree(Matrix Tree+FWT)

    [题解]#6622. 「THUPC 2019」找树 / findtree(Matrix Tree+FWT) 之前做这道题不理解,有一点走火入魔了,甚至想要一本近世代数来看,然后通过人类智慧思考后发现, ...

  4. 洛谷比赛 「EZEC」 Round 4

    洛谷比赛 「EZEC」 Round 4 T1 zrmpaul Loves Array 题目描述 小 Z 有一个下标从 \(1\) 开始并且长度为 \(n\) 的序列,初始时下标为 \(i\) 位置的数 ...

  5. 【转】具透 | 你可能不知道,iOS 10 有一个中国「特供」的联网权限功能

    9 月底,苹果正式在北京成立了苹果中国研发中心.近几年,我们也在每年更新的 iOS 系统中不断看到,苹果对中国市场的关照.从早前的九宫格输入法,到最近的骚扰电话拦截,都照顾了国内用户的需求. 在 iO ...

  6. Windows 10 如何使用「系统还原」功能备份系统状态和配置

    https://www.sysgeek.cn/windows-10-system-restore/ 在 Windows 10 系统中,「系统还原」功能旨在创建配置快照,并在检测到系统更改时将其工作状态 ...

  7. 「POJ 3666」Making the Grade 题解(两种做法)

    0前言 感谢yxy童鞋的dp及暴力做法! 1 算法标签 优先队列.dp动态规划+滚动数组优化 2 题目难度 提高/提高+ CF rating:2300 3 题面 「POJ 3666」Making th ...

  8. LOJ #2538. 「PKUWC 2018」Slay the Spire (期望dp)

    Update on 1.5 学了 zhou888 的写法,真是又短又快. 并且空间是 \(O(n)\) 的,速度十分优秀. 题意 LOJ #2538. 「PKUWC 2018」Slay the Spi ...

  9. 「LOJ#10051」「一本通 2.3 例 3」Nikitosh 和异或(Trie

    题目描述 原题来自:CODECHEF September Challenge 2015 REBXOR 1​​≤r​1​​<l​2​​≤r​2​​≤N,x⨁yx\bigoplus yx⨁y 表示 ...

随机推荐

  1. IDEA修改XML注释风格

    作为一个强迫症患者,每次想在xml文件用快捷键注释的时候,它自动生成的注释一直都是这样的:这令我非常难受,于是每次我都要把光标移到前面,然后再Tab以下,让它变成这样可是每次都这样,好麻烦啊,如果自己 ...

  2. 【PS】证件照转换背景色

    证件照转换背景色 2019-07-14  12:18:49  by冲冲 1. 需求 自由切换证件照的背景颜色(白底.蓝底.红底...) 2. 步骤 ① 双击 图层锁 解锁,弹出的"新建图层0 ...

  3. Java 中的监控与管理原理概述

    点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 程序猿阿朗博客 已经收录,有很多知识点和系列文章. 当前 ...

  4. 3D-DNA 挂载染色体

    3D-DNA是一款简单,方便的处理Hi-C软件,可将contig提升到染色体水平.其githup网址:https://github.com/theaidenlab/3d-dna 3D-DNA流程简介 ...

  5. quota

    一.什么是磁盘配额 磁盘配额从字面意思上看就是给一个磁盘配置多少额度,而quota就是有多少限额的意思,所以总的来说就是限制用户对磁盘空间的使用量.因为Linux是多用户多任务的操作系统,许多人公用磁 ...

  6. Dango之form校验组件

    目录 1.引入案例 2. form组件的功能 3. form组件的使用 3.1 自定义form校验类 3.2 校验数据 3.3 渲染页面 3.4 展示错误信息 3.5 自定义校验结果 3.6 form ...

  7. 日常Java 2021/10/11

    抽象类 所有对象都是通过类描述的,但不是所有的类都是用来描述对象,就好比抽象类,此类中没有足够的信息描述一个对象. 抽象类不能实例化对象,所以抽象类必须的继承,才可以使用. 抽象方法 Abstract ...

  8. Java偏向锁浅析

    偏向锁的定义 顾名思义,偏向锁会偏向第一个访问锁的线程. 如果在接下来的运行过程中,该锁没有被其他线程访问,这持有偏向锁的线程将永远不需要同步 如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线 ...

  9. Learning Spark中文版--第五章--加载保存数据(2)

    SequenceFiles(序列文件)   SequenceFile是Hadoop的一种由键值对小文件组成的流行的格式.SequenceFIle有同步标记,Spark可以寻找标记点,然后与记录边界重新 ...

  10. Shell【常用知识总结】

    一.常用知识总结 1.特殊变量($0,@,#,*,?) $0:当前脚本的文件名. $n:n是一个数字,表示第几个参数. $#:传递给脚本或函数的参数个数. $*:传递给脚本或函数的所有参数.当被双引号 ...