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 turned out to be empty — there was no picture on them. Parts with picture on it are denoted by 1, while empty parts are denoted by 0. Rows of the puzzle are numbered from top to bottom with integers from 1 to n, while columns are numbered from left to right with integers from 1 to m.

Animals decided to complete the picture and play with it, as it might be even more fun! Owl and hedgehog ask each other some queries. Each query is provided by four integers x1, y1, x2, y2 which define the rectangle, where (x1, y1) stands for the coordinates of the up left cell of the rectangle, while (x2, y2) stands for the coordinates of the bottom right cell. The answer to the query is the size of the maximum square consisting of picture parts only (only parts denoted by 1) and located fully inside the query rectangle.

Help Sonya and Filya answer t queries.

Input

The first line of the input contains two integers n and m (1 ≤ n, m ≤ 1000) — sizes of the puzzle.

Each of the following n lines contains m integers aij. Each of them is equal to 1 if the corresponding cell contains a picture and 0 if it‘s empty.

Next line contains an integer t (1 ≤ t ≤ 1 000 000) — the number of queries.

Then follow t lines with queries‘ descriptions. Each of them contains four integers x1, y1, x2, y2 (1 ≤ x1 ≤ x2 ≤ n, 1 ≤ y1 ≤ y2 ≤ m) — coordinates of the up left and bottom right cells of the query rectangle.

Output

Print t lines. The i-th of them should contain the maximum size of the square consisting of 1-s and lying fully inside the query rectangle

Sample Input

3 4
1 1 0 1
0 1 1 0
0 1 1 0
5
1 1 2 3
2 1 3 2
3 2 3 4
1 1 3 4
1 2 3 4

Sample Output

1
1
1
2
2

Hint

题意

给你一个01矩阵,然后Q次询问,每次询问一个矩形区域中,最大的全一正方形的边长是多少。

题解:

此题分为两部分:

1.求出以每个点为右下角的最大正方形边长。

2.二分答案

第一个部分用dp即可,应该不难吧dp[i][j]=min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1。

第二个部分,假如要求有没有边长为len的正方形,左上角有一部分点的边长肯定不合法,合法区间为(x1+len-1,y1+len-1)~(x2,y2),用二维倍增求最大值即可。

二维线段树也是可以的,我只后再补上。

代码

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<iostream>
 4 #include<cstring>
 5 using namespace std;
 6 #define ll long long
 7 #define N 1005
 8 int n,m,x1,y1,x2,y2;
 9 int dp[N][N],mx[N][N][10][10],mm[N];
10 template<typename T>void read(T&x)
11 {
12     ll k=0; char c=getchar();
13     x=0;
14     while(!isdigit(c)&&c!=EOF)k^=c==‘-‘,c=getchar();
15     if (c==EOF)exit(0);
16     while(isdigit(c))x=x*10+c-‘0‘,c=getchar();
17     x=k?-x:x;
18 }
19 void read_char(char &c)
20 {while(!isalpha(c=getchar())&&c!=EOF);}
21 void init_ST()
22 {
23     for(int i=2;i<=max(n,m);i++)mm[i]=mm[i>>1]+1;
24     for(int i=1;i<=n;i++)
25         for(int j=1;j<=m;j++)
26             mx[i][j][0][0]=dp[i][j];
27     for(int k=1;k<=10;k++)
28         for(int i=1;i<=n;i++)
29             for(int j=1;j+(1<<k)-1<=m;j++)
30                 mx[i][j][0][k]=max(mx[i][j][0][k-1],mx[i][j+(1<<(k-1))][0][k-1]);
31     for(int k1=1;k1<=10;k1++)
32         for(int k2=0;k2<=10;k2++)
33             for(int i=1;i+(1<<k1)-1<=n;i++)
34                 for(int j=1;j+(1<<k2)-1<=m;j++)
35                     mx[i][j][k1][k2]=max(mx[i][j][k1-1][k2],mx[i+(1<<(k1-1))][j][k1-1][k2]);
36 }
37 int get_max(int x1,int y1,int x2,int y2)
38 {
39     int k1=mm[x2-x1+1],k2=mm[y2-y1+1];
40     x2=x2-(1<<k1)+1;
41     y2=y2-(1<<k2)+1;
42     int a1=max(mx[x1][y1][k1][k2],mx[x1][y2][k1][k2]);
43     int a2=max(mx[x2][y1][k1][k2],mx[x2][y2][k1][k2]);
44     return max(a1,a2);
45 }
46 bool check(int mid)
47 {
48     return get_max(x1+mid-1,y1+mid-1,x2,y2)>=mid;
49 }
50 int ef(int l,int r)
51 {
52     if (l==r)return l;
53     int mid=(l+r+1)>>1;
54     if (check(mid))return ef(mid,r);
55     else return ef(l,mid-1);
56 }
57 int main()
58 {
59 #ifndef ONLINE_JUDGE
60     freopen("aa.in","r",stdin);
61 #endif
62     read(n); read(m);
63     for(int i=1;i<=n;i++)
64         for(int j=1;j<=m;j++)
65         {
66             int x;
67             read(x);
68             if (x==0)continue;
69             dp[i][j]=min(min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;
70         }
71     init_ST();
72     int q;
73     read(q);
74     while(q--)
75     {
76         read(x1); read(y1); read(x2); read(y2);
77         int ans=ef(0,min(x2-x1+1,y2-y1+1));
78         printf("%d\n",ans);
79     }
80 }

原文地址:https://www.cnblogs.com/mmmqqdd/p/10836974.html

时间: 2024-10-11 23:26:59

Codeforces Round #371 (Div. 1) D. Animals and Puzzle 二维倍增的相关文章

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 Round #198 (Div. 1) D. Iahub and Xors 二维树状数组*

D. Iahub and Xors Iahub does not like background stories, so he'll tell you exactly what this problem asks you for. You are given a matrix a with n rows and n columns. Initially, all values of the matrix are zeros. Both rows and columns are 1-based,

Codeforces Round #371 (Div. 1)

A: 题目大意: 在一个multiset中要求支持3种操作: 1.增加一个数 2.删去一个数 3.给出一个01序列,问multiset中有多少这样的数,把它的十进制表示中的奇数改成1,偶数改成0后和给出的01序列相等(比较时如果长度不等各自用0补齐) 题解: 1.我的做法是用Trie数来存储,先将所有数用0补齐成长度为18位,然后就是Trie的操作了. 2.官方题解中更好的做法是,直接将每个数的十进制表示中的奇数改成1,偶数改成0,比如12345,然后把它看成二进制数10101,还原成十进制是2

Codeforces Round #371 (Div. 2)B. Filya and Homework

题目链接:http://codeforces.com/problemset/problem/714/B 题目大意: 第一行输入一个n,第二行输入n个数,求是否能找出一个数x,使得n个数中的部分数加上x或部分数减去x ,n个数相等. 例如:5 1 3 3 2 1 输出: YES Init: x=1 情况,第一个数和最后一个数+x,第二三个数减去x ,则这n个数都为2(相等),故输出“YES” 解题思路: 对于刚才举的例子 可知 n个数只有三个元素 1 2 3 只要使得 2-1==3-2 即可满足条

Codeforces Round #371 (Div. 2) C

传送门  map或者字典数的应用 简单题 题意: 思路: AC代码: 1 #include<iostream> 2 #include<cstring> 3 #include<cmath> 4 #include<map> 5 #include<algorithm> 6 #include<cstdio> 7 #define max(a,b) a>b?a:b 8 #define F(i,a,b) for(int i=a;i<=b

Codeforces Round #371 (Div. 2) A

传送门 题意: 思路: AC代码: 1 #include "iostream" 2 #include "string.h" 3 #include "stack" 4 #include "queue" 5 #include "map" 6 #include "algorithm" 7 #include "stdio.h" 8 #include "math.h&

Codeforces Round #371 (Div. 2) C. Sonya and Queries(字典树)

题目戳这 题意:给你一个数字 n ,然后 n 组输入,如果第一个字符是+,就把后面的那个数字加入到集合里面去,如果第一个字符是减号,就把后面的那个数字从集合里面去掉一个,如果是问好,就开始配对,看看有多少个数字是和问号后面的数字是匹配的,是否配对的规则是,问好后面的数字都是二进制,一表示奇数,零表示偶数,如果集合中的数字长度和二进制的输入长度不一样,就把短的那个在前面加零,比如,二进制是101,集合中的数字是12,那就是101和012匹配,如果二进制是11,集合中的数字是12345,那么就是00

Codeforces Round #371 (Div. 2) B

传送门 题意:给你串数字 任意你取一个x 对某些数进行加x 某些数进行减x操作 是否能使得数组内的数全部相等 ps:某些数里的某些可能为0 思路:先排序 然后确定数组内有多少不同的数 大于3一定不能 小于2一定能 坑点在等于3 当数组里的数成等差数列时可以 否则不可以 例如:1 4 7 x=3: 1+3 = 4: 7-3 = 4: 1 3 4 就不可以了 AC代码: 1 #include "iostream" 2 #include "string.h" 3 #inc

Codeforces Round #326 (Div. 1) - C. Duff in the Army 树上倍增算法

题意:一个n个点的数, m个人住在其中的某些点上, 每个人的标号1-m, 询问u-v 路径上标号前a个人,并输出标号,a < 10. 作法, 利用倍增, ID[j][i] 表示i到i的第2^j个祖先上前10个人, 那么每次询问直接维护就好了,细节好多, 刚开始不知道怎么求ID[j][i]. 这里把2^j分成两部分, 前2^(j-1)和 后2^(j-1)个, 然后递推的维护. 感觉树链剖分也可以做, 不知道会不会TLE, 树链剖分的话 线段树的每个点维护10个值, 每次合并就行了. #includ