F Planting Trees

题目链接

https://ac.nowcoder.com/acm/contest/883/F

题目描述

The semester is finally over and the summer holiday is coming. However, as part of your university's graduation requirement, you have to take part in some social service during the holiday. Eventually, you decided to join a volunteer group which will plant trees in a mountain.

To simplify the problem, let's represent the mountain where trees are to be planted with an \(N \times N\) grid. Let's number the rows \(\ 1\) to \(\ N\) from top to bottom, and number the columns \(\ 1\) to \(\ N\) from left to right. The elevation of the cell in the \(\ i\)-th row and \(\ j\)-th column is denoted by \(a_{i,j}\). Your leader decides that trees should be planted in a rectangular area within the mountain and that the maximum difference in elevation among the cells in that rectangle should not exceed M. In other words, if the coordinates of the top-left and the bottom-right corners of the rectangle are \((x_1,y_1)\) and \((x_2,y_2)\), then the condition $ |a_{i,j} - a_{k,l}| \le M$ must hold for \(x_1 \le i,k \le x_2, \ y_1 \le j,l \le y_2\). Please help your leader calculate the maximum possible number of cells in such a rectangle so that he'll know how many trees will be planted.

输入描述:

The input contains multiple cases. The first line of the input contains a single integer \(T\ (1 \le T \le 1000)\), the number of cases.

For each case, the first line of the input contains two integers $ N\ (1 \le N \le 500)$ and \(M\ (0 \le M \le 10^5)\). The following N lines each contain N integers, where the\(\ j-th\) integer in the i\ i i-th line denotes \(a_{i,j} \ (1 \le a_{i,j} \le 10^5)\).

It is guaranteed that the sum of \(N^3\) over all cases does not exceed \(25 \cdot 10^7\).

输出描述:

For each case, print a single integer, the maximum number of cells in a valid rectangle.

输入

    2
2 0
1 2
2 1
3 1
1 3 2
2 3 1
3 2 1

输出

    1
4

题意

给你一个\(N \times N\)的矩阵,求最大的自矩阵满足子矩阵中最大值和最小值只差小于m.

题解

一看体面最后一句话,寻思着就是\(n^3\)的算法,于是很自然地想到\(n^2\)枚举上届和下界,再用\(n\)来扫描一遍。

我们想想假如上下界确定了,我们再枚举左边界,右边界越长越好,那么左边界往右移时,右边界一定不会往左移(这里应该不难想,只要想肯定不难想到),这个好像就叫做尺取法。

剩下的就是要求区间的最小值和最大值了,我一开始直接二维倍增,结果超时了,改了两个小时还是超时,只能放弃了。之后用了单调队列来求最小值和最大值,这种做法感觉很经典,但是我竟然没看出来,还是做少了题目.

简要地讲讲单调队列:

我们搞两个队列,一个用来求最大值,另一个用来求最小值

第一步:去掉不合法的队首(更改左边界)

第二部:往后延伸右边界

第三部:更新答案

这么讲有点抽象,之后我去找找基础的题目,做完基础题目,这个自然也就会了。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x7f7f7f7f
#define N 505
#define maxx(a,b) a<b?b:a
#define minn(a,b) a<b?a:b
int n,m,mx[N],mi[N],a[N][N];
struct Queue{int val,id;}Q1[N],Q2[N];
clock_t now;
template<typename T>void read(T&x)
{
ll k=0; char c=getchar();
x=0;
while(!isdigit(c)&&c!=EOF)k^=c=='-',c=getchar();
if (c==EOF)exit(0);
while(isdigit(c))x=x*10+c-'0',c=getchar();
x=k?-x:x;
}
inline void work()
{
now=clock();
int ans=1;
read(n); read(m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
read(a[i][j]);
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
{
int ds1=1,dw1=0,ds2=1,dw2=0,r=0;
for(int k=1;k<=n;k++)
{
if (i==j)mx[k]=mi[k]=a[i][k];
else {mx[k]=maxx(mx[k],a[j][k]);mi[k]=minn(mi[k],a[j][k]);}
}
for(int k=1;k<=n;k++)
{
r=maxx(r,k-1);
while(ds1<=dw1&&Q1[ds1].id<k)ds1++;
while(ds2<=dw2&&Q2[ds2].id<k)ds2++;
if (mx[k]-mi[k]>m)continue;
int m1=maxx(mx[r+1],Q1[ds1].val);
int m2=minn(mi[r+1],Q2[ds2].val);
if (ds1>dw1)m1=mx[r+1],m2=mi[r+1];
while(r+1<=n&&(mx[r+1]-mi[r+1]<=m)&&(ds1>dw1||m1-m2<=m))
{
while(mx[r+1]>Q1[dw1].val&&ds1<=dw1)dw1--;
while(mi[r+1]<Q2[dw2].val&&ds2<=dw2)dw2--;
Q1[++dw1]={mx[r+1],r+1};
Q2[++dw2]={mi[r+1],r+1};
r++;
m1=maxx(m1,mx[r+1]);
m2=minn(m2,mi[r+1]);
}
ans=maxx(ans,(j-i+1)*(r-k+1));
} }
printf("%d\n",ans);
//cout<<(double)(clock()-now)/CLOCKS_PER_SEC*1000<<endl;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
int T;
read(T);
while(T--)work();
}

超时代码(仅供观赏)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x7f7f7f7f
#define N 505
int n,m,mx[N][N][10][10],mi[N][N][10][10],mm[N],mv[10];
template<typename T>void read(T&x)
{
ll k=0; char c=getchar();
x=0;
while(!isdigit(c)&&c!=EOF)k^=c=='-',c=getchar();
if (c==EOF)exit(0);
while(isdigit(c))x=x*10+c-'0',c=getchar();
x=k?-x:x;
}
void read_char(char &c)
{while(!isalpha(c=getchar())&&c!=EOF);}
void build_ST()
{
for(int i=2;i<=n;i++)mm[i]=mm[i>>1]+1;
for(int i=0;i<=10;i++)mv[i]=1<<i;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
mx[i][j][0][0]=mi[i][j][0][0];
for(int k=1;k<=8;k++)
for(int i=1;i<=n;i++)
for(int j=1;j+(1<<k)-1<=n;j++)
{
mx[i][j][0][k]=max(mx[i][j][0][k-1],mx[i][j+(1<<(k-1))][0][k-1]);
mi[i][j][0][k]=min(mi[i][j][0][k-1],mi[i][j+(1<<(k-1))][0][k-1]);
} for(int k1=1;k1<=8;k1++)
for(int k2=0;k2<=8;k2++)
for(int i=1;i+(1<<k1)-1<=n;i++)
for(int j=1;j+(1<<k2)-1<=n;j++)
{
mx[i][j][k1][k2]=
max(mx[i][j][k1-1][k2],mx[i+(1<<(k1-1))][j][k1-1][k2]);
mi[i][j][k1][k2]=
min(mi[i][j][k1-1][k2],mi[i+(1<<(k1-1))][j][k1-1][k2]);
} }
inline int get_mx(int x1,int y1,int x2,int y2)
{
int k1=mm[x2-x1+1],k2=mm[y2-y1+1];
x2=x2-mv[k1]+1;
y2=y2-mv[k2]+1;
int a1=max(mx[x1][y1][k1][k2],mx[x1][y2][k1][k2]);
int a2=max(mx[x2][y1][k1][k2],mx[x2][y2][k1][k2]);
return max(a1,a2);
}
inline int get_mi(int x1,int y1,int x2,int y2)
{
int k1=mm[x2-x1+1],k2=mm[y2-y1+1];
x2=x2-(1<<k1)+1;
y2=y2-(1<<k2)+1;
int a1=min(mi[x1][y1][k1][k2],mi[x1][y2][k1][k2]);
int a2=min(mi[x2][y1][k1][k2],mi[x2][y2][k1][k2]);
return min(a1,a2);
}
void work()
{
int ans=1;
read(n); read(m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
read(mi[i][j][0][0]);
build_ST();
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
{
int l=1;
for(int k=1;k<=n;k++)
{
int mx=get_mx(i,l,j,k);
int mi=get_mi(i,l,j,k);
while(mx-mi>m&&l<=k)
{
l++;
if(l<=k)mx=get_mx(i,l,j,k);
if(l<=k)mi=get_mi(i,l,j,k);
}
ans=max(ans,(j-i+1)*(k-l+1));
}
}
printf("%d\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
int T;
read(T);
while(T--)work();
}

2019牛客暑期多校训练营(第三场)F Planting Trees 单调队列的更多相关文章

  1. 2019牛客暑期多校训练营(第三场) F.Planting Trees(单调队列)

    题意:给你一个n*n的高度矩阵 要你找到里面最大的矩阵且最大的高度差不能超过m 思路:我们首先枚举上下右边界,然后我们可以用单调队列维护一个最左的边界 然后计算最大值 时间复杂度为O(n*n*n) # ...

  2. 2019牛客暑期多校训练营(第三场)- F Planting Trees

    题目链接:https://ac.nowcoder.com/acm/contest/883/F 题意:给定n×n的矩阵,求最大子矩阵使得子矩阵中最大值和最小值的差值<=M. 思路:先看数据大小,注 ...

  3. 2019牛客暑期多校训练营(第一场)-A (单调栈)

    题目链接:https://ac.nowcoder.com/acm/contest/881/A 题意:给定两个长度均为n的数组a和b,求最大的p使得(a1,ap)和(b1,bp)等价,等价的定义为其任意 ...

  4. 2019牛客暑期多校训练营(第三场)H题目

    题意:给你一个N×N的矩阵,求最大的子矩阵 满足子矩阵中最大值和最小值之差小于等于m. 思路:这题是求满足条件的最大子矩阵,毫无疑问要遍历所有矩阵,并判断矩阵是某满足这个条件,那么我们大致只要解决两个 ...

  5. 2019牛客暑期多校训练营(第九场)A:Power of Fibonacci(斐波拉契幂次和)

    题意:求Σfi^m%p. zoj上p是1e9+7,牛客是1e9:  对于这两个,分别有不同的做法. 前者利用公式,公式里面有sqrt(5),我们只需要二次剩余求即可.     后者mod=1e9,5才 ...

  6. 2019牛客暑期多校训练营(第一场)A题【单调栈】(补题)

    链接:https://ac.nowcoder.com/acm/contest/881/A来源:牛客网 题目描述 Two arrays u and v each with m distinct elem ...

  7. 2019牛客暑期多校训练营(第一场) B Integration (数学)

    链接:https://ac.nowcoder.com/acm/contest/881/B 来源:牛客网 Integration 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 5242 ...

  8. 2019牛客暑期多校训练营(第一场) A Equivalent Prefixes ( st 表 + 二分+分治)

    链接:https://ac.nowcoder.com/acm/contest/881/A 来源:牛客网 Equivalent Prefixes 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/ ...

  9. 2019牛客暑期多校训练营(第二场)F.Partition problem

    链接:https://ac.nowcoder.com/acm/contest/882/F来源:牛客网 Given 2N people, you need to assign each of them ...

  10. 2019牛客暑期多校训练营(第一场)A Equivalent Prefixes(单调栈/二分+分治)

    链接:https://ac.nowcoder.com/acm/contest/881/A来源:牛客网 Two arrays u and v each with m distinct elements ...

随机推荐

  1. 2019.6.24 校内测试 NOIP模拟 Day 2 分析+题解

    看到Day 2的题真的想打死zay了,忒难了QwQ~ T1 江城唱晚 这明显是个求方案数的计数问题,一般的套路是DP和组合数学. 正如题目中所说,这个题是一个 math 题.      ----zay ...

  2. 二十三、Linux任务计划及周期性任务执行:at、crontab命令

    一.概述 未来的某时间点执行一次某任务:at, batch周期性运行某任务:crontab 这两个任务的执行结果:会通过邮件发送给用户 (本地终端用户之间的邮件通知) centos 5,6,7默认开启 ...

  3. docker运行puppeteer出现Page crash解决方案

    Docker默认文件空间64MB.如果puppeteer运行的时候超过这个内存就出现了.Page crash.可以使用docker run --shm-size=256m指定一个更大的内存即可.

  4. pwn学习日记Day18 《程序员的自我修养》读书笔记

    知识杂项 obj文件:当前源代码编译成二进制目标文件 exe文件:将.obj文件与库文件.lib等文件链接生成的可执行文件 一个现代编译器的主要工作流程如下: 源程序(source code)→ 预处 ...

  5. php-fpm(绕过open_basedir,结合ssrf)

    环境的安装->https://www.cnblogs.com/zaqzzz/p/11870489.html 1.nginx的畸形访问 因为安装的是php7.0,所以需要手动修改一下(版本低的时候 ...

  6. ubuntu hadoop安装

    参考: https://wangchangchung.github.io/2017/09/28/Ubuntu-16-04%E4%B8%8A%E5%AE%89%E8%A3%85Hadoop%E5%B9% ...

  7. 黑马vue---61、为什么vue组件的data要是一个函数

    黑马vue---61.为什么vue组件的data要是一个函数 一.总结 一句话总结: 因为js中以函数为变量作用域,所以这样可以保证每个组件的数据不互相影响 二.why components data ...

  8. mongoose 实现 增、删、改、查

    mongoose常用的API 增 save是一个实例方法,使用时需要先 new Model() 来实例化 //保存一个用户信息,userobj为你创建的文档对象模型里的字段,需正确对应传入 const ...

  9. java多线程面试题整理及答案(2019年)

    1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对 运算密集型任务提速.比如,如果一个线程完 ...

  10. Cobbler安装报错--dhcpd -t failed Exception occured: <class 'cobbler.cexceptions.CX'>

    最近想玩一下自动化安装,就研究了一下cobbler,测试环境本来是想用CentOS6.X的,但是后来一想,还是想用CentOS7.X.然后就碰到坑了. 在同步cobbler配置的时候,发现了以下错误: ...