codeforces 713D Animals and Puzzle

题意:

给出n * m 的0/1矩阵,然后q组询问,问询问的矩形中包含的全1正方形的最大边长。

题解:

首先考虑暴力算法,对于每个询问的矩形,在里面暴力找到全1正方形,现在的问题就是怎么找全1正方形,可以递推实现,dp[i][j]表示以(i, j)作为右下角的全一正方形最长边长。

dp[i][j] = min (dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1。

while (T--) {
        int x1, y1, x2, y2;
        scanf ("%d%d%d%d", &x1, &y1, &x2, &y2);
        int ans = 0;
        for (int i = x1; i <= x2; ++i) {
            for (int j = y1; j <= y2; ++j) {
                int len = dp[i][j];
                if (i - len + 1 < x1 && j - len + 1 < y1) ans = max (ans, min(i - x1 + 1, j - y1 + 1));
                else if (j - len + 1 < y1) ans = max (ans, j - y1 + 1);
                else if (i - len + 1 < x1) ans = max (ans, i - x1 + 1);
                else ans = max (ans, len);
            }
        }
        cout << ans << endl;
    }

  

现在考虑,对每个询问的两个for循环优化:

那么现在的问题就是维护一个矩形中最大的dp值是多少,二维线段树?不不不,TLE,复杂度(T * (logn)^3),显然可以用二维ST表

但是会发现有很多情况会越过边界从而不符合条件,可以二分出矩形的全1正方形的边长,根据定义那么问题就等价于在(x1 + mid - 1, y1 + mid -1)到 (x2, y2)中寻找最大的dp值,就直接拿最大值和二分值比较就好了,就可以不用考虑是否越界的问题了。O(∩_∩)O~

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;

const int N = 1e3 + 100;
int n, m, dp[11][11][N][N], T, lg[N];

void BuildSt () {
	lg[1] = 0;
	for (int i = 2; i <= 1000; ++i) lg[i] = lg[i >> 1] + 1;
	for (int x = 1; x <= n; ++x)
		for (int i = 1; i <= 10; ++i)
			for (int y = 1; y <= m; ++y)
				if (y + (1<<i-1) <= m) dp[0][i][x][y] = max (dp[0][i-1][x][y], dp[0][i-1][x][y+(1<<i-1)]);
	for (int i = 1; i <= 10; ++i)
		for (int x = 1; x <= n; ++x)
			for (int j = 0; j <= 10; ++j)
				for (int y = 1; y <= m; ++y)
					if (x + (1<<i-1) <= n) dp[i][j][x][y] = max (dp[i-1][j][x][y], dp[i-1][j][x+(1<<i-1)][y]);
}

int query (int x1, int y1, int x2, int y2) {
	int lx = lg[x2 - x1 + 1], ly = lg[y2 - y1 + 1];
	x2 = x2 - (1 << lx) + 1;
	y2 = y2 - (1 << ly) + 1;
	return max (max (dp[lx][ly][x1][y1], dp[lx][ly][x1][y2]), max (dp[lx][ly][x2][y1], dp[lx][ly][x2][y2]));
}

int main () {
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			int x;
			scanf ("%d", &x);
			if (x == 1)dp[0][0][i][j] = min (dp[0][0][i-1][j], min (dp[0][0][i][j-1], dp[0][0][i-1][j-1])) + 1;
		}
	}
	BuildSt();
	scanf ("%d", &T);
	while (T--) {
		int x1, y1, x2, y2;
		scanf ("%d%d%d%d", &x1, &y1, &x2, &y2);
		int l = 0, r = min (x2 - x1 + 1, y2 - y1 + 1);
		while (l < r) {
			int mid = l + r + 1 >> 1;
			if (query(x1 + mid - 1, y1 + mid - 1, x2, y2) >= mid) l = mid;
			else r = mid - 1;
		}
		cout << l << endl;
	}
	return 0;
}

  

总结:

蒟蒻涨姿势了~学到了二维的ST,还有这道题的边界处理也非常巧妙,而且我不知道下次我能不能想到,总之先记住这个套路吧。。。

时间: 2024-08-03 15:39:21

codeforces 713D Animals and Puzzle的相关文章

Codeforces Round #371 (Div. 1) D. Animals and Puzzle 二维倍增

D. Animals and Puzzle 题目连接: http://codeforces.com/contest/713/problem/D Description Owl Sonya gave a huge lake puzzle of size n × m to hedgehog Filya as a birthday present. Friends immediately started to assemble the puzzle, but some parts of it turn

Codeforces Round #371 (Div. 1) D - Animals and Puzzle 二维ST表 + 二分

D - Animals and Puzzle #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PII pair<int, int> #define PLI pair<LL, int> #define ull unsigned long long using namespace std; const in

codeforces 713D D. Animals and Puzzle 二分+二维rmq

题目链接 给一个01矩阵, 然后每个询问给出两个坐标(x1, y1), (x2, y2). 问你这个范围内的最大全1正方形的边长是多少. 我们dp算出以i, j为右下角的正方形边长最大值. 然后用二维st表预处理出所有的最大值. 对于每个询问, 我们二分一个值mid, 查询(x1 + mid -1, y1 + mid -1), (x2, y2)这个范围内的最大值是否大于mid .如果大于的话就说明在(x1, y1), (x2, y2)范围内存在一个边长为mid的正方形. #include <bi

CodeForces 35D Animals

G - Animals Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice CodeForces 35D Description input input.txt output output.txt Once upon a time DravDe, an outstanding person famous for his professional ac

codeforces713D Animals and Puzzle(二维倍增)

引自:http://www.cnblogs.com/qscqesze/p/5929117.html 题意: 给你一个01矩阵,然后Q次询问,每次询问一个矩形区域中,最大的全一正方形的边长是多少. 思路: 首先考虑Dp,dp[i][j]表示以(i,j)位置为右下角,最大的正方形边长是多少,显然dp[i][j]=min(dp[i-1][j],dp[j][i-1],dp[i-1][j-1])+1 然后我们做出这个dp之后,我们怎么做呢? 直接二分答案,假设我们二分的答案为mid,显然在这个矩形区域的左

Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论

Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论 题意 给你一段数,然后小明去猜某一区间内的gcd,这里不一定是准确值,如果在这个区间内改变一个数的值(注意不是真的改变),使得这个区间的gcd是小明所猜的数也算小明猜对.另一种操作就是真的修改某一点的值. 解题思路 这里我们使用线段树,维护区间内的gcd,判断的时候需要判断这个区间的左右子区间的gcd是不是小明猜的数的倍数或者就是小明猜的数,如果是,那么小明猜对了.否则就需要进入这个区间

Codeforces Round #467 (Div. 2) E -Lock Puzzle

Lock Puzzle 题目大意:给你两个字符串一个s,一个t,长度<=2000,要求你进行小于等于6100次的shift操作,将s变成t, shift(x)表示将字符串的最后x个字符翻转后放到最前面. 思路:不会写,看了题解... 因为长度为3000,操作为6500,我们考虑每三次操作将一个字符放到最后,并保证其他字符的顺序不变,这样是可以实现的, 如果我们想要将第k个字符移到最后,我们只要shift(n-1-k) , shift(1) , shift(n-1),就能实现啦 . 1 #incl

Codeforces 627F Island Puzzle - 构造

题目传送门 传送门I 传送门II 传送门III 题目大意 (八数码问题的树上版本). 已经存在解的时候直接输出最少的移动步数. 否则允许加入一条边,要求输出边的端点以及最少的移动步数. 仍然无解输出-1. 不添加边的时候方案是唯一的:直接把0移动到目的地,然后判断是否有解. 考虑把$0$移动到目的后,把0当做根.因为操作可逆,这个时候是否存在解与原图是否存在解等价. 考虑此时0走到一个环上转移圈的意义:把进入点先拿出来,转上若干圈,回到起点,把原来的进入点放回环中. 所以所有不合法的位置只有两种

codeforces 696B puzzle

求随机dfs序的期望. 我们来看到一个点之后是什么情况. 一个点p有很多儿子,其中一个是x: 如果要求x的期望,那么先看: 如果从父亲直接到这个点,那就是父亲的期望加一: 如果还走其他的点,那么最后一步肯定还是要加一(因为你总要花一步从别的店跳过来),所以无论怎样都要父亲的期望加一.剩下来再看,如果跳到别的点,那么就是说明有附加的期望.下面再加上这一部分就行了. 那么现在就是求多出来的这一部分期望. 怎么求呢,首先看清一个问题. 设p0=(son_num[p]-son_num[x])/(all_