【YBTOJ】【Luogu P4398】[JSOI2008]Blue Mary的战役地图

链接:

洛谷

博客园

题目大意:

求两个矩阵的最大公共子正方形矩阵的边长。

(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;
}

原文地址:https://www.cnblogs.com/GJY-JURUO/p/14731121.html