ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NKOJ8856 矩形

2021-11-14 10:33:53  阅读:134  来源: 互联网

标签:NKOJ8856 frac int bitset 1505 矩形 omega


给出一个 \(n\times n\) 的网格,有些格子是黑色的剩下是白色的。有 \(m\) 次询问:询问网格中有多少 \(a\) 行 \(b\) 列的矩形,满足它的四条边上所有格子都是黑的。

注意:空间限制为128M。

\(1\le n,m\le 1500\)

最暴力的做法:每次询问都枚举矩形的左上角,前缀和判一下是否四条边都是黑的,\(O(mn^2)\) 直接去世。
那么我们考虑对于一组给定的 \(a,b\),我们知道哪些信息就能快速求出答案。

首先,满足要求的矩形上面的边和下面的边是全黑的。因此我们枚举上面的边和下面的边所处的行,对每一行维护一个 bitset,第 \(i\) 行的 bitset 第 \(j\) 位为 \(1\) 表示 \((i,j),(i,j+1)...(i,j+a-1)\) 全为黑色。记第 \(i\) 行的 bitset 为 \(s_i\)。
那么满足上面的边和下面的边是全黑的,且上面的边在第 \(i\) 行,下面的边在第 \(j\) 行的矩形数量为 \(\vert s_i\and s_j\vert\)。
这一步需要枚举上下的边所在行,然后还要 bitset 操作一下,时间复杂度 \(O(\frac{n^3}{\omega})\)。

但这个东西对于固定的 \(a\) 才有用,当 \(a\) 发生变化时,\(s\) 也会变。
所以将询问离线下来按 \(a\) 排序,为了方便接下来处理 \(s\) 的变化,我们再对于每一行维护一个 bitset 叫做 \(d\),\(d_{i,j}=1\) 表示 \((i,j)\) 为黑色,\(d_{i,j}=0\) 表示 \((i,j)\) 为白色。
每当 \(a\) 加 \(1\) 时,\(s_i\&=d_i>>a-1\)。这里 \(a\) 指变化后的 \(a\)。右移是因为 bitset 是从高位到低位存的。
每次修改耗时 \(O(\frac{n^2}{\omega})\),总共进行 \(n\) 次修改,总耗时 \(O(\frac{n^3}{\omega})\)。

以上算的都是上面的边和下面的边,我们还要考虑左右的边。

先考虑左边的边。由于上面我们枚举了矩形上下的边,记上面的边为 \(l\),下面的边为 \(r\),因此如果一个左上角 \((l,i)\) 满足条件,则 \(a_{l,i}=1,a_{l+1,i}=1...a_{r,i}=1\)。

到这里就有一个想法呼之欲出:把 \(l\) 行到 \(r\) 行的 \(d\) 求交集,得到的是左边全是黑色的矩形左上角列号集合;再把它右移 \(b-1\) 位,得到右边全是黑色的矩形左上角列号集合;再把这两个集合与 \(s_l,s_r\) 求交集,求得的集合就是满足要求的矩形左上角列号集合。

但是这样做复杂度是 \(O(\frac{n^4}{\omega})\) 的,原地起飞。

我们考虑优化求 \(l\) 行到 \(r\) 行的 \(d\) 的交集这一步。区间集合快速求交,直接用 st 表就行了!好恶心啊其实写起来非常非常短。

那这样复杂度 \(O(\frac{n^3+mn^2}{\omega})\),费了好大力气就比暴力快了一个 \(\omega\) 但它就是过了

nkoj 这题时限开了 5s,我跑了 2s 不到,放其它 OJ 开 1s 其实绰绰有余。

顺便提一句,这题官方题解做法又臭又长又慢,这个做法是机房巨佬想出来的

#include <cstdio>
#include <bitset>
#include <algorithm>

struct Quest {
	int a, b, id;
	inline bool operator < (const Quest x) const {return b < x.b;}
} q[1505];

std::bitset<1505> s1[1505], s2[1505], st[1505][12], tmp;
char str[1505];
int Log[1505], ans[1505];

int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++ i) {
		scanf("%s", str);
		for (int j = 0; j < n; ++ j) s1[i][j] = str[j] - '0';
		st[i][0] = s1[i], s2[i].set();
	}
	for (int i = 2; i <= n; ++ i) Log[i] = Log[i >> 1] + 1;
	for (int j = 1; 1 << j <= n; ++ j)
	for (int i = 1; i + (1 << j) - 1 <= n; ++ i)
		st[i][j] = st[i][j - 1] & st[i + (1 << j - 1)][j - 1];
	for (int i = 1; i <= m; ++ i) scanf("%d%d", &q[i].a, &q[i].b), q[i].id = i;
	std::sort(q + 1, q + m + 1);
	for (int i = 1; i <= m; ++ i) {
		for (int j = q[i - 1].b + 1; j <= q[i].b; ++ j)
			for (int k = 1; k <= n; ++ k) s2[k] &= (s1[k] >> j - 1);
		int d = Log[q[i].a];
		for (int j = 1; j + q[i].a - 1 <= n; ++ j) {
			int k = j + q[i].a - 1;
			tmp = st[j][d] & st[k - (1 << d) + 1][d];
			ans[q[i].id] += (s2[j] & s2[k] & tmp & tmp >> q[i].b - 1).count();
		}
	}
	for (int i = 1; i <= m; ++ i) printf("%d\n", ans[i]);
	return 0;
}

标签:NKOJ8856,frac,int,bitset,1505,矩形,omega
来源: https://www.cnblogs.com/stinger/p/15550908.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有