[JOI Open 2023] 花园 / Garden 题解

[JOI Open 2023] 花园 / Garden 题解


知识点

单调队列。

分析

首先可以看出所有的坐标范围都只需要在 \([0,2D)\) 取到即可。

这类求矩形面积相关的题目有一个套路:枚举左下角,那么它的两维范围是 \([0,D)\)

A 类比较简单,它的存在只是让我们固定了最小范围,比如一个 \((P,Q)\),假设现在有一个点 \((x,y)\),如果它作为矩形左下角,那么右上角 \((x',y')\) 需要满足:

  1. \(x\) 轴:
    • \(x\le P\)\(x'\ge P\)
    • \(x > P\)\(x'\ge P+D\)
  2. \(y\) 轴:
    • \(y\le Q\)\(y'\ge Q\)
    • \(y > Q\)\(y'\ge Q+D\)

所以 A 类可以开两个 \(O(D)\) 的数组 \(AX_x,AY_y\) 用前缀最值预处理出来。

B 类其实也一样,对于一个 \((R,S)\),假设现在有一个点 \((x,y)\),如果它作为矩形左下角,那么右上角 \((x',y')\) 需要满足以下两个条件的其中一个

  1. \(x\) 轴:
    • \(x\le R\)\(x'\ge R\)
    • \(x > R\)\(x'\ge R+D\)
  2. \(y\) 轴:
    • \(y\le S\)\(y'\ge S\)
    • \(y > S\)\(y'\ge S+D\)

现在枚举左下角 \((x,y)\)

考虑单调队列做法,枚举每个 \(x\) 值,\(O(M)\) 处理出每个 \(y\) 对应的最小 \(x\)\(BX_y\):对于一个 \((R,S)\),我们将其 \(S\) 记在 \(R-1,R+D-1\) 两个值上,枚举 \(y\) 时,我们将它滑动窗口的范围设为 \([y,y+D-1)\),然后枚举单调队列中的值即可。

那么我们得到了一个 \(O(D^3+DM)\) 的算法。

发现每次 \(O(M)\) 预处理出的数组有很多重复部分,可以离线,每个 \((R,S)\) 只需在 \(0,R+1\) 两个 \(x\) 轴坐标分别做两次修改。

然后单调队列中的值只需要在 \(y\) 删除的时候与 \(y-1\) 做一次更新即可,容易看出这样是最优的,\(O(D^3)\) 变为 \(O(D^2)\)

注意有一些边界条件要特殊处理。

总复杂度 \(O(N+M+D^2)\)

代码

constexpr int N(5e5+10),_D(5e3+10);

int n,m,D,ans;
int AX[_D],AY[_D],BX[_D<<1];
vector<int> vec[_D];

int cal(int xa,int xb,int ya,int yb) { return (xb-xa+1)*(yb-ya+1); }

signed main() {
#ifdef plus_cat
	freopen(plus_cat ".in","r",stdin),freopen(plus_cat ".out","w",stdout);
#endif // plus_cat
	/*DE("Input");*/
	scanf("%d%d%d",&n,&m,&D),ans=D*D;
	/*DE("Solve A class");*/
	FOR(i,1,n) {
		int x,y;
		scanf("%d%d",&x,&y);
		tomax(AX[0],x),tomax(AX[x+1],x+D);
		tomax(AY[0],y),tomax(AY[y+1],y+D);
	}
	FOR(i,1,D)tomax(AX[i],i,AX[i-1]),tomax(AY[i],i,AY[i-1]);
	/*DE("Record B class");*/
	FOR(y,0,2*D-1)BX[y]=0;
	FOR(i,1,m) {
		int x,y;
		scanf("%d%d",&x,&y);
		if(y>0)tomax(BX[y-1],x);
		tomax(BX[y+D-1],x),vec[x+1].push_back(y);
	}
	/*DE("Solve");*/
	FOR(x,0,D-1) {
		tomin(ans,cal(x,AX[x],0,D-1));
		for(int y:vec[x]) {
			if(y>0)tomax(BX[y-1],x+D-1);
			tomax(BX[y+D-1],x+D-1);
		}
		int it(0),h(1),t(0);
		static int q[_D<<1];
		auto update=[&](int i,int y) {
			if(y<0)return;
			int _y(max(q[i-1]+1,AY[y]));
			tomin(ans,cal(x,max(AX[x],BX[q[i]]),y,_y));
		};
		FOR(y,0,D-1) {
			while(it<y+D-1) {
				while(h<=t&&BX[it]>BX[q[t]])update(t--,y-1);
				q[++t]=it++;
			}
			while(h<=t&&q[h]<AY[y])update(h++,y-1);
		}
		while(h<=t)update(h++,D-1);
	}
	/*DE("Output");*/
	printf("%d\n",ans);
	return 0;
}
posted @ 2025-11-16 19:54  Add_Catalyst  阅读(13)  评论(0)    收藏  举报