题解:P2216 [HAOI2007] 理想的正方形
elainya_stars · · 题解
P2216 [HAOI2007] 理想的正方形 题解
猫猫切得最快的绿题,
注:本文中所有双引号都是为了断句,无特殊意义。
题意
给定
思路
注,下面是求
:::info[考虑暴力]{open}
首先,遍历每个点作为正方形右下角,再遍历正方形每个点求
:::
:::info[考虑优化]{open}
显然,块内的最大值是“每行中元素最大值”的最大值,所以可以预处理每个长度为 我不知道能不能过。
:::
:::info[继续优化]{open}
预处理之后的部分看起来很好优化的样子!那就再来一次预处理吧!
既然我们预处理好后还需遍历行下标求
时间
:::info[以样例为例的所有数组]
a=5,b=4,n=2;
输入的p[i][j]
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
以(i,j)为右端点,长度为n的长条内的最值
maxn[i][j] minn[i][j]
-1 2 5 6 -1 1 2 5
-1 17 17 16 -1 0 16 0
-1 17 17 2 -1 16 2 1
-1 10 10 2 -1 2 2 1
-1 2 2 2 -1 1 2 2
以(i,j)为右下角,边长为n的正方形内的最值
maxl[i][j] minl[i][j]
-1 -1 -1 -1 -1 -1 -1 -1
-1 17 17 16 -1 0 2 0
-1 17 17 16 -1 0 2 0
-1 17 17 2 -1 2 2 1
-1 10 10 2 -1 1 2 1
“-1”代表无法构造长条或正方形
:::
:::
我表达能力怎么这么差, 看代码吧,自认为马蜂良好。
Code
:::success[by elainya_stars]
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fastio ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define nbsp cerr<<'\n'
#define debugx(x) cerr<<#x<<" : "<<x<<'\n'
#define debuga(x,l,r) cerr<<#x<<" : ";for(int i=l;i<=r;i++)cerr<<x[i]<<' ';cerr<<'\n'
#define debugp(x,i) cerr<<#x<<'['<<i<<']'<<" : "<<x[i]<<'\n'
#define flin(s) char ccccc1[100]=s;char ccccc2[5]=".in";freopen(strcat(ccccc1,ccccc2),"r",stdin)
#define flout(s) char ccccc3[100]=s;char ccccc4[5]=".out";freopen(strcat(ccccc3,ccccc4),"w",stdout)
#define clr(x,y) memset(x,y,sizeof x)
const int N=1005,inf=0x3f3f3f3f3f3f3f3f;
int a,b,n,p[N][N],maxn[N][N],minn[N][N];
// maxn minn用来存第一次预处理的结果:
// 以(i,j)为右端点,长度为n的长条内的最大最小值
int maxl[N][N],minl[N][N];
// maxl minl用来存第二次预处理的结果:
// 以(i,j)为右下角,边长为n的正方形内的最大最小值
struct queue_ // 手搓队列
{
int ll=1,rr,a[N];
void pushl(int x) {a[++ll]=x;}
void pushr(int x) {a[++rr]=x;}
void popl() {ll++;}
void popr() {rr--;}
int l() {return a[ll];}
int r() {return a[rr];}
bool empty() {return ll>rr;}
}q;
signed main()
{
fastio;
cin>>a>>b>>n;
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
cin>>p[i][j];
clr(maxn,-1),clr(minn,-1),clr(maxl,-1),clr(minl,-1);
// 全赋-1,因为元素有可能为0
for(int i=1;i<=a;i++) // 行下标
{
q.ll=1,q.rr=0; // 清队
for(int j=1;j<=b;j++)
{
while(!q.empty() && q.l()<j-n+1)
q.popl();
while(!q.empty() && p[i][q.r()]<=p[i][j])
q.popr();
q.pushr(j);
// 上面单调队列板子
if(j>=n) // 小于n时不可构造长条,值还为-1
maxn[i][j]=p[i][q.l()]; // 赋值
}
q.ll=1,q.rr=0; // 同理
for(int j=1;j<=b;j++)
{
while(!q.empty() && q.l()<j-n+1)
q.popl();
while(!q.empty() && p[i][q.r()]>=p[i][j])
q.popr();
q.pushr(j);
if(j>=n)
minn[i][j]=p[i][q.l()];
}
}
for(int j=1;j<=b;j++) // 列下标
{
if(maxn[1][j]==-1) // 如果为-1,不可构造正方形
continue;
q.ll=1,q.rr=0; // 和上面同理,行列颠倒
for(int i=1;i<=a;i++)
{
while(!q.empty() && q.l()<i-n+1)
q.popl();
while(!q.empty() && maxn[q.r()][j]<=maxn[i][j])
q.popr();
q.pushr(i);
if(i>=n)
maxl[i][j]=maxn[q.l()][j];
}
q.ll=1,q.rr=0;
for(int i=1;i<=a;i++)
{
while(!q.empty() && q.l()<i-n+1)
q.popl();
while(!q.empty() && minn[q.r()][j]>=minn[i][j])
q.popr();
q.pushr(i);
if(i>=n)
minl[i][j]=minn[q.l()][j];
}
}
int ans=inf;
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
{
if(maxl[i][j]==-1) // 若为-1,不可构造正方形
continue;
ans=min(ans,maxl[i][j]-minl[i][j]);
}
cout<<ans<<'\n';
return (0.0);
}
:::
给我赞赞 qwq