P2601 [ZJOI2009] 对称的正方形
涉及知识:
哈希、Manacher,没了。
做法:
题意非常清晰,无需赘述,直接讲做法。
看到对称想到 Manacher,不妨考虑将一维的 Manacher 略略扩展,考虑到类似于枚举中心的思路。我们只要对每一行都跑一次 Manacher 便可以达成这个要求。
考虑一维和二维的不同之处,在转移时我们原本所需要考虑的“相同的一对点”变成了“相同的两对线”,即上与下,左与右。想到这里就已经可以写出一份保证有正确性的代码了。
然而我们分析复杂度,每一次比较四条线是 \(O(n+m)\) 的,而每一行都跑一次 Manacher 是 \(O(nm)\) 的,加起来总共是 \(O(n^3)\) 的,显然无法通过此题。
我们考虑优化,发现其实 Manacher 已经没有什么好优化的地方了,于是优化比较的过程,发现比较连续序列是否相等可以用字符串哈希 \(O(1)\) 来做,但是值域偏大,数又多,容易冲突,考虑离散化,可以将值域降低到 \(10^6\) 的大小,比较安全,单哈希自然溢出直接通过。
哈希因为是二维,预处理时间复杂度 \(O(n m)\),算上 Manacher 的 \(O(n m)\),总复杂度 \(O(n m)\)。
注意:
首先是不要记录不合法的答案,记录方形半径的数组只会在 \(x\)、\(y\) 同为奇数或偶数时才有贡献,不用管。
其次是不要看成中心对称,虽然我觉得只有我会看成这个。
Code:
感觉我的码算比较短的了(码风不毒),注释写的还算详细,总之看得愉快!
点击查看代码
#include <bits/extc++.h>
#define e_ putchar(' ')
#define en_ putchar('\n')
using uint = unsigned int; // 好像c++多少后就直接有这个不用定义了,但是想到dev会报错,所以还是定义了
using namespace std;
template <typename T> inline T in(T &n) {
n = 0; char p = getchar();
while(p < '-') p = getchar();
// bool f = p == '-' ? p = getchar() : 0;
do n = (n << 1) + (n << 3) + (p ^ 48), p = getchar();
while(isdigit(p));
// return n = f ? -n : n;
return n;
}
template <typename T> inline void out(T x) {
if(x < 0) putchar('-'), x = -x;
if (x >= 10) out(x / 10);
putchar(x % 10 + '0');
}
constexpr int N = 2000 + 100;
constexpr uint p = 1007;
uint wd[N][N], wr[N][N], pp[N], wl[N][N], wu[N][N];
int s[N][N], l[N][N], n, m;
long long ans;
vector<int> t;
inline void pre() { // 预处理哈希
pp[0] = 1;
for(int i = 1; i < N; i ++) pp[i] = pp[i - 1] * p;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
wr[i][j] = s[i][j] + wr[i][j - 1] * p;
for(int i = 1; i <= n; i ++)
for(int j = m; j >= 1; j --)
wl[i][j] = s[i][j] + wl[i][j + 1] * p;
for(int j = 1; j <= m; j ++)
for(int i = 1; i <= n; i ++)
wd[i][j] = wd[i - 1][j] * p + s[i][j];
for(int j = 1; j <= m; j ++)
for(int i = n; i >= 1; i --)
wu[i][j] = wu[i + 1][j] * p + s[i][j];
}
inline bool check(int i, int j, int len) { // 同样比较丑陋的比较
if(i - len < 1 or i + len > n or j + len > m or j - len < 1) return 0;
uint a = wr[i - len][j + len] - wr[i - len][j - len - 1] * pp[len + len + 1],
b = wr[i + len][j + len] - wr[i + len][j - len - 1] * pp[len + len + 1],
c = wd[i + len][j + len] - wd[i - len - 1][j + len] * pp[len + len + 1],
d = wd[i + len][j - len] - wd[i - len - 1][j - len] * pp[len + len + 1],
e = wl[i - len][j - len] - wl[i - len][j + len + 1] * pp[len + len + 1],
f = wu[i - len][j + len] - wu[i + len + 1][j + len] * pp[len + len + 1];
if(a != b or c != d or a != e or f != c) return 0;
return 1;
}
inline void mnc(int *l, int *s, int c) { // 马拉车
for(int i = 1, o = 0, r = -1; i <= m; i ++) {
l[i] = i <= r ? min(r - i, l[o * 2 - i]) : 1;
while(check(c, i, l[i])) l[i] ++;
if(l[i] + i - 1 > r) r = l[i] + i - 1, o = i;
}
}
signed main() {
in(n), in(m);
n = n * 2 + 1;
m = m * 2 + 1;
for(int i = 2; i <= n; i += 2) // 改完后好看了的读入
for(int j = 2; j <= m; j += 2)
in(s[i][j]), t.push_back(s[i][j]);
sort(t.begin(), t.end()); // 离散化
t.erase(unique(t.begin(), t.end()), t.end());
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
if(!(i & 1) and !(j & 1)) s[i][j] = lower_bound(t.begin(), t.end(), s[i][j]) - t.begin() + 1;
pre(); // 预处理哈希(具体见上)
for(int i = 1; i <= n; i ++) // 给每行跑一次马拉车
mnc(l[i], s[i], i);
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
if((!(i & 1) and !(j & 1)) || ((i & 1) and (j & 1)))
ans += (l[i][j]) / 2; // 统计答案
out(ans), en_;
}
拿了最优解,$p$ 取 $3$ 都过了,难绷
#include <bits/extc++.h>
#ifdef ONLINE_JUDGE
#define getchar getchar_unlocked
#define putchar putchar_unlocked
#endif
#define e_ putchar(' ')
#define en_ putchar('\n')
#define uint unsigned int
using namespace std;
inline int in(int &n) {
n = 0; char p = getchar();
while(p < '-') p = getchar();
do n = (n << 1) + (n << 3) + (p ^ 48), p = getchar();
while(isdigit(p));
return n;
}
inline void out(int x) {
if (x >= 10) out(x / 10);
putchar(x % 10 + '0');
}
const int N = 2000 + 100;
const uint p = 3;
uint wd[N][N], wr[N][N], pp[N];
uint wl[N][N], wu[N][N];
int s[N][N], l[N][N];
int ans, m, n;
inline void pre() {
pp[0] = 1;
for(int i = 1; i < 2010; ++ i) pp[i] = pp[i - 1] * p;
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= m; ++ j)
wr[i][j] = s[i][j] + wr[i][j - 1] * p;
for(int i = 1; i <= n; ++ i)
for(int j = m; j >= 1; -- j)
wl[i][j] = s[i][j] + wl[i][j + 1] * p;
for(int j = 1; j <= m; ++ j)
for(int i = 1; i <= n; ++ i)
wd[i][j] = wd[i - 1][j] * p + s[i][j];
for(int j = 1; j <= m; ++ j)
for(int i = n; i >= 1; -- i)
wu[i][j] = wu[i + 1][j] * p + s[i][j];
}
inline bool check(int i, int j, int le) {
if(i - le < 1 or i + le > n or j + le > m or j - le < 1) return 0;
uint a = wr[i - le][j + le] - wr[i - le][j - le - 1] * pp[le + le + 1],
b = wr[i + le][j + le] - wr[i + le][j - le - 1] * pp[le + le + 1],
c = wd[i + le][j + le] - wd[i - le - 1][j + le] * pp[le + le + 1],
d = wd[i + le][j - le] - wd[i - le - 1][j - le] * pp[le + le + 1],
e = wl[i - le][j - le] - wl[i - le][j + le + 1] * pp[le + le + 1],
g = wu[i - le][j + le] - wu[i + le + 1][j + le] * pp[le + le + 1];
if(a != b or c != d or a != e or g != c) return 0;
return 1;
}
inline void mnc(int *l, int *s, int c) {
int o = 0, r = -1;
for(int i = 1; i <= m; ++ i) {
l[i] = i <= r ? min(r - i, l[o * 2 - i]) : 1;
while(check(c, i, l[i])) ++ l[i];
if(l[i] + i - 1 > r) r = l[i] + i - 1, o = i;
}
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("in.in", "r", stdin);
#endif
in(n), in(m);
n = n * 2 + 1;
m = m * 2 + 1;
for(int i = 2; i <= n; i += 2)
for(int j = 2; j <= m; j += 2)
in(s[i][j]);
pre();
for(int i = 1; i <= n; ++ i)
mnc(l[i], s[i], i);
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= m; ++ j)
if((!(i & 1) and !(j & 1)) || ((i & 1) and (j & 1)))
ans += (l[i][j]) >> 1;
out(ans), en_;
}

浙公网安备 33010602011771号