• 【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;
    }
    
    
  • 相关阅读:
    WM_MOUSEWHEEL消息的处理
    Delphi之TStrings和TStringLists类[转]
    使用mysqladmin命令修改Mysql密码
    AP_发票与预付款核销时预付款带税码
    ORACLE EBS AP发票到付款的数据流
    .关于货币大写的探索
    Oracle SQL 空值排序(Nulls)
    实现主从关系Form中汇总行金额/数量
    巧妙的查看FORM中的 LOV查询语句
    供应商接口的使用
  • 原文地址:https://www.cnblogs.com/GJY-JURUO/p/14731121.html
Copyright © 2020-2023  润新知