链接:
题目大意:
求两个矩阵的最大公共子正方形矩阵的边长。
(nleq 50)。
正文:
暴力是 (mathcal{O}(n^7)),考虑优化暴力。
既然是找“公共”,那么一定会判相同,可以用二维 Hash 和 Hash 表解决。
然后就有了一个 (mathcal{O}(n^3)) 的暴力:枚举正方形右下角点和其边长。继续优化它,可以发现,其实可以通过二分边长把时间复杂度降到 (mathcal{O}(n^2log n))。
有一点需要注意:Hash 表的空间不是 (n^2) 的,而是 (sum_{i=1}^n i^2=frac{n(n+1)(2n+1)}{6}) 的,因为所有边长的正方形都要考虑到。
代码:
const int N = 60;
inline ll READ()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
unsigned ll base1 = 87, base2 = 37;
unsigned ll fac1[N], fac2[N];
struct HASH
{
unsigned ll h[N][N];
HASH() {memset(h, 0, sizeof h);}
inline unsigned ll* operator [] (int a) {return h[a];}
}H[2];
const ll mod = 19260817ll;
struct Hash
{
unsigned ll val;
int nxt, len;
}h[1ll * N * (N + 1) * (2 * N + 1) / 6];
int head[mod + 10], tot;
void add(unsigned ll x, int len)
{
ll y = x % mod;
h[++tot] = (Hash){x, head[y], len}, head[y] = tot;
}
bool query(unsigned ll x, int len)
{
ll y = x % mod;
for (int i = head[y]; i; i = h[i].nxt)
if (h[i].val == x && h[i].len == len) return 1;
return 0;
}
int n;
ll a[N][N], b[N][N];
unsigned ll calc(int x1, int y1, int x2, int y2, int len, int f)
{
return H[f][x2][y2] - H[f][x1 - 1][y2] * fac2[len] -
H[f][x2][y1 - 1] * fac1[len] + H[f][x1 - 1][y1 - 1] * fac1[len] * fac2[len];
}
bool check(int x)
{
for (int i = x; i <= n; i++)
for (int j = x; j <= n; j++)
add(calc(i - x + 1, j - x + 1, i, j, x, 0), x);
for (int i = x; i <= n; i++)
for (int j = x; j <= n; j++)
if(query(calc(i - x + 1, j - x + 1, i, j, x, 1), x))
return 1;
return 0;
}
int main()
{
n = READ();
fac1[0] = fac2[0] = 1ull;
for (int i = 1; i <= n; i++)
fac1[i] = fac1[i - 1] * base1,
fac2[i] = fac2[i - 1] * base2;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
a[i][j] = READ();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
b[i][j] = READ();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
H[0][i][j] += H[0][i][j - 1] * base1 + a[i][j],
H[1][i][j] += H[1][i][j - 1] * base1 + b[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
H[0][i][j] += H[0][i - 1][j] * base2,
H[1][i][j] += H[1][i - 1][j] * base2;
int l = 1, r = n, mid, ans = 0;
while (l <= r)
{
mid = l + r >> 1;
if (check(mid)) l = mid + 1, ans = max(ans, mid);
else r = mid - 1;
}
printf("%d
", ans);
return 0;
}