题解 [IOI2008]Pyramid Base 金字塔地基

题目链接
题意描述

你有一片n×m的网格,其中有P个障碍(x1,y1,x2,y2),并有一个移除的费用Ci,你有B的预算用于移除障碍,求修建一座正方形金字塔的最大边长。
1n,m106,Ci3×103
三组Subtask:
Subtask1:B=0,1P103
Subtask2:1<B2×109,1P3×104
Subtask3:B=0,1P4×105

Solution:
两类Subtask区别较大(一种无预算,但要求O(nlogn),一种有预算,但数据范围游刃有余),考虑分别求解。


Algorithm I:
既然没有预算,那么就可以直接将障碍扫描线,对于m开一棵线段树,维护最大子段。主函数需要O(n)的算法,如果知道左边界i,那么我们希望右边界j尽量大,又因为要求正方形,因此还要保证线段树上最大子段ji+1,容易想到two pointers不断移动j,在这个过程中取max即可。

点击查看代码
namespace Subtask1{
	vector<int>Line[M][2];
	namespace SGT{
		struct Data{int lmax,rmax,Max,Len,Tag,Add;}tr[M<<2];
		void Pushup(int k){
			if(tr[k].Add>=1)return void(tr[k].lmax=tr[k].rmax=tr[k].Max=0);
			if(tr[k].Len==1)return void(tr[k].lmax=tr[k].rmax=tr[k].Max=1);
			tr[k].Len=tr[ls].Len+tr[rs].Len;
			tr[k].lmax=(tr[ls].lmax==tr[ls].Len)?tr[ls].Len+tr[rs].lmax:tr[ls].lmax;
			tr[k].rmax=(tr[rs].rmax==tr[rs].Len)?tr[rs].Len+tr[ls].rmax:tr[rs].rmax;
			tr[k].Max=max(tr[ls].rmax+tr[rs].lmax,max(tr[ls].Max,tr[rs].Max));
		}
		void Build(int k,int l,int r){
			if(l==r){
				tr[k].lmax=tr[k].rmax=tr[k].Max=tr[k].Len=1;
				return;
			}
			Build(ls,l,mid);
			Build(rs,mid+1,r);
			Pushup(k);
		}
		void Modify(int k,int l,int r,int x,int y,int z){
			if(l>=x&&r<=y){
				tr[k].Add+=z;
				Pushup(k);
				return;
			}
			if(x<=mid)Modify(ls,l,mid,x,y,z);
			if(mid<y)Modify(rs,mid+1,r,x,y,z);
			Pushup(k);
		}
	}
	using namespace SGT;
	void Solve(){
		Build(1,1,m);
		for(int i=1;i<=P;i++)Line[a[i].x[0]][0].pb(i),Line[a[i].x[1]][1].pb(i);
		for(int i=1,j=0;i<=n;i++){
			while(tr[1].Max>j-i&&j<=n){
				j++;
				for(auto k:Line[j][0])
					Modify(1,1,m,a[k].y[0],a[k].y[1],1);
			}
			ans=max(ans,j-i);
			for(auto k:Line[i][1])
				Modify(1,1,m,a[k].y[0],a[k].y[1],-1);
		}
		printf("%d\n",ans);
	}
}

Algorithm II:
上来显然先二分答案ans,对于每一行i,考虑维护该行以(i,j)为左下角的正方形,这些正方形已经固定,那么只需要判定是否有一个正方形内所有有交的障碍的预算之和B,即维护全局最小值。
一个障碍对正方形的影响范围为(x1l+1,y1l+1,x2,y2),可以再次使用扫描线维护。

点击查看代码
namespace Subtask2{
	Obstacle b[N];
	vector<int>Line[M][2];
	namespace SGT{
		struct Data{int Min,Tag;}tr[M<<2];
		void Pushup(int k){tr[k].Min=min(tr[ls].Min,tr[rs].Min);}
		void Update(int k,int v){tr[k].Tag+=v;tr[k].Min+=v;}
		void Pushdown(int k){
			if(tr[k].Tag!=0){
				Update(ls,tr[k].Tag);
				Update(rs,tr[k].Tag);
				tr[k].Tag=0;
			}
		}
		void Build(int k,int l,int r){
			tr[k].Min=tr[k].Tag=0;
			if(l==r)return;
			Build(ls,l,mid);
			Build(rs,mid+1,r);
		}
		void Modify(int k,int l,int r,int x,int y,int z){
			if(l>=x&&r<=y){
				Update(k,z);
				return;
			}
			Pushdown(k);
			if(x<=mid)Modify(ls,l,mid,x,y,z);
			if(mid<y)Modify(rs,mid+1,r,x,y,z);
			Pushup(k);
		}
	}
	using namespace SGT;
	bool check(int x){
		Build(1,1,m-x+1);
		for(int i=1;i<=n-x+1;i++)Line[i][0].clear(),Line[i][1].clear();
		for(int i=1;i<=P;i++){
			b[i]=a[i];
			b[i].x[0]=max(b[i].x[0]-x+1,1);
			b[i].y[0]=max(b[i].y[0]-x+1,1);
			b[i].x[1]=min(b[i].x[1],n-x+1);
			b[i].y[1]=min(b[i].y[1],m-x+1);
			if(b[i].x[0]>b[i].x[1]||b[i].y[0]>b[i].y[1])continue;
			Line[b[i].x[0]][0].pb(i);
			Line[b[i].x[1]][1].pb(i);
		}
		for(int i=1;i<=n-x+1;i++){
			for(auto j:Line[i][0])
				Modify(1,1,m-x+1,b[j].y[0],b[j].y[1],b[j].C);
			if(tr[1].Min<=B)return true;
			for(auto j:Line[i][1])
				Modify(1,1,m-x+1,b[j].y[0],b[j].y[1],-b[j].C);
		}
		return false;
	}
	void Solve(){
		int l=1,r=min(n,m),ans=0;
		while(l<=r){
			if(check(mid))ans=mid,l=mid+1;
			else r=mid-1;
		}
		printf("%d\n",ans);
	}
}
posted @   pidan007  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示