知らないデータ構造が出て勉強になった。
http://codeforces.com/contest/713/problem/D
問題
N*Mのグリッドが与えられる。各マスは白黒に塗り分けられている。
このグリッドに対し、T個のクエリが与えられる。
各クエリは、グリッド中の矩形領域で構成される。矩形内で、最大サイズの塗りつぶされた正方形を答えよ。
解法
二分探索と2D Sparse Tableで解く。(2D SegTreeでも解けるかもしれないが計算量的に不安)
まず、各セル(x,y)に対し、そのセルを左上とする最大の正方形サイズS(x,y)を求めておこう。
事前に累積和を求めておくと尺取法の要領でこの部分はO(NM)で計算できる。
クエリの範囲を(x1,y1)-(x2,y2)とする。
この範囲にサイズaの正方形があるのなら、(x1,y1)-(x2-(a-1),y2-(a-1))の範囲に、S(x,y)≧aとなる(x,y)があればよい。
これは矩形範囲内の値の最大値を求めることに相当する。
自分は知らなかったが、2D Sparse Tableというデータ構造を用いると、前処理でO(NM*logN*logM)かかるがクエリをO(1)で解くことができる。
2D SegTreeよりメモリを食うが、今回はTが大きいのでこちらを用いた方が無難。
http://codeforces.com/blog/entry/45485
あとはsに対しては二分探索すればよい。
template<class V,int NV> class RMQ_2D { private: V table[NV][NV][1<<NV][1<<NV]; int LG[1<<NV]; int NV2; public: static V const def=-1<<30; V comp(V l,V r){ return max(l,r);}; RMQ_2D() { int i,j,x,y; NV2=1<<NV; for(i=2;i< NV2;i++) LG[i]=LG[i/2]+1; FOR(i,NV) FOR(j,NV) FOR(x,NV2) FOR(y,NV2) table[i][j][x][y]=def; } void set(int x,int y,V v){ table[0][0][x][y]=v;} void build() { int i,j,x,y; FOR(i,NV) FOR(j,NV) FOR(x,NV2) FOR(y,NV2) { if(i<NV-1) table[i+1][j][x][y]=comp(table[i][j][x][y],(x+(1<<i)<NV2)?table[i][j][x+(1<<i)][y]:def); if(j<NV-1) table[i][j+1][x][y]=comp(table[i][j][x][y],(y+(1<<j)<NV2)?table[i][j][x][y+(1<<j)]:def); } } V query(int L,int R,int T,int B) { //[L,R), [T,B) L=max(0,L), R=min(R,NV2), T=max(0,T), B=min(B,NV2); if(R<=L || B<=T) return def; int WL=LG[R-L],HL=LG[B-T]; return comp(comp(table[WL][HL][L][T],table[WL][HL][R-(1<<WL)][T]), comp(table[WL][HL][L][B-(1<<HL)],table[WL][HL][R-(1<<WL)][B-(1<<HL)])); } }; RMQ_2D<int,10> rmq; int H,W,Q; int A[1010][1010]; int S[2010][2010]; int T[1010][1010]; int yes(int y,int x,int t) { return (S[y+t][x+t]-S[y+t][x]-S[y][x+t]+S[y][x])==t*t; } void solve() { int i,j,k,l,r,x,y; string s; scanf("%d%d",&H,&W); FOR(y,H) FOR(x,W) { scanf("%d",&A[y][x]); S[y+1][x+1]=S[y+1][x]+S[y][x+1]-S[y][x]+A[y][x]; } FOR(y,H) FOR(x,W) { if(A[y][x]) { T[y][x]=1; if(y&&x) T[y][x]=max(T[y][x],T[y-1][x-1]-1); while(yes(y,x,T[y][x]+1)) T[y][x]++; } rmq.set(x,y,T[y][x]); } rmq.build(); scanf("%d",&Q); while(Q--) { int L,R,T,B; scanf("%d%d%d%d",&L,&T,&R,&B); L--,T--; x=0; for(i=9;i>=0;i--) { y=x+(1<<i); if(rmq.query(T,B-(y-1),L,R-(y-1))>=y) x=y; } _P("%d\n",x); } }
まとめ
これみんな知ってるテクだったのか…。