对称正方形
题目链接:ybt高效进阶2-2-3 / luogu P2601
题目大意
给你一个矩阵,问你这个矩阵中上下对称且左右对称的正方形子矩阵的个数。
思路
这道题就是模拟很烦而已。
首先,很明显要用 hash 来做,因为是矩形,是二维的。那就要用二维的 hash。
(具体就是搞两个质数,先把每一行用一个质数 hash 一下,然后在一列一列按另一个质数 hash)
`
至于得到一个区间的 hash 值(假设是
[
l
x
,
l
y
]
∼
[
r
x
,
r
y
]
[lx,ly]\sim[rx,ry]
[lx,ly]∼[rx,ry],两个质数分别是
z
1
,
z
2
z1,z2
z1,z2),就是:
h
a
s
h
r
x
,
r
y
−
h
a
s
h
r
x
,
l
y
−
1
×
z
1
r
y
−
l
y
+
1
−
h
a
s
h
l
x
−
1
,
r
y
×
z
2
r
x
−
l
x
+
1
+
h
a
s
h
l
x
−
1
,
l
y
−
1
×
z
1
r
y
−
l
y
+
1
×
z
2
r
x
−
l
x
+
1
hash_{rx,ry}-hash_{rx,ly-1}\times z1^{ry-ly+1}-hash_{lx-1,ry}\times z2^{rx-lx+1}+hash_{lx-1,ly-1}\times z1^{ry-ly+1}\times z2^{rx-lx+1}
hashrx,ry−hashrx,ly−1×z1ry−ly+1−hashlx−1,ry×z2rx−lx+1+hashlx−1,ly−1×z1ry−ly+1×z2rx−lx+1
那你考虑一下一个左右上下对称的正方形要满足什么特点。
那很明显,对称就是按着对称轴翻转过来它还是一样的。
那就是这个正方形左右反过来,上下反过来所形成的图形和原来都一样。
那你就构造出最大的矩形的两个翻转图形,然后看看原来的位置应该变道哪里。
原来是
[
x
,
y
]
[x,y]
[x,y](假设),那左右翻转就是
[
x
,
m
−
y
+
1
]
[x,m-y+1]
[x,m−y+1],上下翻转就是
[
n
−
x
+
1
,
y
]
[n-x+1,y]
[n−x+1,y]。
那我们再看矩形翻转之后的位置变化。
假设原来是
[
l
x
,
l
y
]
∼
[
r
x
,
r
y
]
[lx,ly]\sim[rx,ry]
[lx,ly]∼[rx,ry],那左右翻转的就是
[
l
x
,
m
−
r
y
+
1
]
∼
[
r
x
,
m
−
r
x
+
1
]
[lx,m-ry+1]\sim[rx,m-rx+1]
[lx,m−ry+1]∼[rx,m−rx+1],上下翻转的就是
[
n
−
r
x
+
1
,
l
y
]
∼
[
n
−
l
x
+
1
,
l
y
]
[n-rx+1,ly]\sim[n-lx+1,ly]
[n−rx+1,ly]∼[n−lx+1,ly]。
那我们就可以枚举矩阵的中心点,然后我们可以把这些矩阵分成两种。要么是长度是奇数的,要么是长度是偶数的。
你可以发现,对于某一种矩阵,如果你对于这个中心的长度为
x
x
x 的矩阵是对称的,那对于这种矩阵,这个中心的长度小于等于
x
x
x 的矩阵都是对称的。
那我们就发现它满足二分的单调性,按我们可以分别把两种矩阵二分出个数。
然后我们就得到答案了。
代码
#include<cstdio>
#include<iostream>
#define di1 1000000007ull
#define di2 1000000009ull
#define ull unsigned long long
using namespace std;
int n, m, a[1001][1001], matrix_up[1001][1001], matrix_left[1001][1001], l, r, mid, ans, tot, lx, ly, tmp;
ull hash[1001][1001], times1[1001], times2[1001], hash_up[1001][1001], hash_left[1001][1001], hash1, hash2, hash3;
bool ch(int rx, int ry, int dis) {
lx = rx - dis + 1;//这个是普通的矩阵
ly = ry - dis + 1;
hash1 = hash[rx][ry] - hash[rx][ly - 1] * times1[dis] - hash[lx - 1][ry] * times2[dis] + hash[lx - 1][ly - 1] * times1[dis] * times2[dis];
tmp = rx;//这个是上下翻转的矩阵
rx = n - (rx - dis);
lx = rx - dis + 1;
ly = ry - dis + 1;
hash2 = hash_up[rx][ry] - hash_up[rx][ly - 1] * times1[dis] - hash_up[lx - 1][ry] * times2[dis] + hash_up[lx - 1][ly - 1] * times1[dis] * times2[dis];
rx = tmp;//这个是左右翻转的矩阵
ry = m - (ry - dis);
lx = rx - dis + 1;
ly = ry - dis + 1;
hash3 = hash_left[rx][ry] - hash_left[rx][ly - 1] * times1[dis] - hash_left[lx - 1][ry] * times2[dis] + hash_left[lx - 1][ly - 1] * times1[dis] * times2[dis];
if (hash1 == hash2 && hash1 == hash3) return 1;
return 0;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
scanf("%d", &a[i][j]);
matrix_up[n - i + 1][j] = a[i][j];//得到两种翻转的矩阵
matrix_left[i][m - j + 1] = a[i][j];
}
times1[0] = 1ull;//求出来后面有用
for (int i = 1; i <= n; i++)
times1[i] = times1[i - 1] * di1;
times2[0] = 1ull;
for (int i = 1; i <= m; i++)
times2[i] = times2[i - 1] * di2;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {//得出矩阵hash值
hash[i][j] = hash[i][j - 1] * di1 + a[i][j];
hash_up[i][j] = hash_up[i][j - 1] * di1 + matrix_up[i][j];
hash_left[i][j] = hash_left[i][j - 1] * di1 + matrix_left[i][j];
}
times1[i] = times1[i - 1] * di1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
hash[i][j] += hash[i - 1][j] * di2;
hash_up[i][j] += hash_up[i - 1][j] * di2;
hash_left[i][j] += hash_left[i - 1][j] * di2;
}
}
for (int i = 1; i <= n; i++)//枚举中心在哪里
for (int j = 1; j <= m; j++) {
ans = 0;
l = 1;
r = min(min(i, n - i + 1), min(j, m - j + 1));
while (l <= r) {//长度是奇数
mid = (l + r) >> 1;
if (i - mid + 1 < 1 || i + mid - 1 > n || j - mid + 1 < 1 || j + mid - 1 > m) {
r = mid - 1;
continue;
}
if (ch(i + mid - 1, j + mid - 1, mid * 2 - 1)) {
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
tot += ans;
ans = 0;
l = 1;
r = min(min(i, n - i), min(j, m - j));
while (l <= r) {//长度是偶数
mid = (l + r) >> 1;
if (i - mid + 1 < 1 || i + mid > n || j - mid + 1 < 1 || j + mid > m) {
r = mid - 1;
continue;
}
if (ch(i + mid, j + mid, mid * 2)) {
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
tot += ans;
}
printf("%d", tot);
return 0;
}