1. 程式人生 > >「CodePlus 2018 3 月賽」尋找車位

「CodePlus 2018 3 月賽」尋找車位

access_globe 有一個巨大的停車場,這個停車場有 nn 行,每行有 mm 個車位。為了美觀,access_globe 在建立這個停車場時,規定這個停車場必須是長條形的,即 n\ge mn≥m。每個車位都是一個正方形的區域。
最近,access_globe 正在為抽不到 Missing Poster 而苦惱,因此他請你幫他維護這個停車場。你需要支援兩個個事件:
一輛車停到某一個車位中,或一輛車從某個車位開走
查詢一個矩形區域內最大的只包含空車位的正方形區域
如果你能幫 access_globe 高效地解決這個問題,access_globe 一定會好好獎勵你的。

具體題面

推薦題解
其實就是(對每一列開一顆)開m顆線段樹,第y顆線段樹x處的值為以x行y列為右下端點的最大正方形大小,線段樹就是維護一下最大值。(推薦結合樣例理解)
然後發現第y顆線段樹 [ l , r ] [l,r]

的值就是l到r行y列為右下端點的最大正方形大小(不考慮外面的行),然後我們來合併 [ l , m i d ] [l,mid]
[ m i d + 1 , r ] [mid+1,r] 的最大正方形大小,我們只需要考慮過mid這條線的正方形,那麼算這條線左邊的拓展,右邊的拓展,再單調佇列掃一遍可以求出O(m) 1 m 1到m 顆線段樹的 [ l , r ] [l,r] 的答案,然後就合併,嗯合併。
O ( m q log n ) O(mq \log n)
AC Code:

#include<bits/stdc++.h>
#define N 4000006
#define lc now<<1
#define rc now<<1|1
using namespace std;

int n,m,q;
struct arr
{int f[4*N];inline int* operator[](int x){ return f+x*m; }}mp,rL,lL,val;
int ql[2005],qr[2005],hl,tl,hr,tr,len[4*N];

inline void Merge(int r,int r1,int r2)
{	hl=hr=tl=tr=0;
	for(int i=1,d=0;i<=m;i++)
	{	for(;hl<tl&&lL[r1][ql[tl-1]]>lL[r1][i];tl--);
		for(;hr<tr&&rL[r2][qr[tr-1]]>rL[r2][i];tr--);
		ql[tl++]=i,qr[tr++]=i;
		for(;hl<tl&&hr<tr&&lL[r1][ql[hl]]+rL[r2][qr[hr]]<i-d;) 
			d++,(ql[hl]<=d)&&(hl++),(qr[hr]<=d)&&(hr++);
		val[r][i]=max(val[r1][i],max(val[r2][i],i-d));}
	for(int i=1;i<=m;i++) 
		lL[r][i]=lL[r2][i]+(lL[r2][i]==len[r2])*lL[r1][i],rL[r][i]=rL[r1][i]+(rL[r1][i]==len[r1])*rL[r2][i];
	len[r]=len[r1]+len[r2];
}
void Modify(int now,int l,int r,int x,int y)
{	if(l==r){ val[now][y]=lL[now][y]=rL[now][y]=(mp[x][y]^=1); return; }
	int mid=(l+r)>>1;x<=mid?(Modify(lc,l,mid,x,y),0):(Modify(rc,mid+1,r,x,y),0);
	Merge(now,lc,rc);}
void Build(int now,int l,int r)
{	if(l==r){ 
		for(int i=1;i<=m;i++) 
			val[now][i]=rL[now][i]=lL[now][i]=mp[l][i];
		len[now]=1;return;}
	int mid=(l+r)>>1;
	Build(lc,l,mid);
	Build(rc,mid+1,r);
	Merge(now,lc,rc);}
int Query(int now,int l,int r,int a,int b,int c,int d)
{	if(r<a || l>c) return 0;	
	if(l>=a && r<=c)
	{	Merge(0,0,now);int ret = 0;
		for(int i=b;i<=d;i++) ret = max(ret,min(i-b+1,max(val[0][i],val[now][i])));
		return ret;}
	int mid = (l+r)>>1;
	return max(Query(rc,mid+1,r,a,b,c,d),Query(lc,l,mid,a,b,c,d));}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]);
	Build(1,1,n);
	for(int op,a,b,c,d;q--;)
	{
		scanf("%d",&op);
		if(op==0)
		{
			scanf("%d%d",&a,&b);
			Modify(1,1,n,a,b);
		}
		else 
		{
			scanf("%d%d%d%d",&a,&b,&c,&d);
			len[0] = 0;
			for(int i=1;i<=m;i++) rL[0][i]=lL[0][i]=val[0][i]=0;
			printf("%d\n",Query(1,1,n,a,b,c,d));
		}
	}
}