Bounding Wall 线段树 + 思维
题目大意:
一张n * m 的图,‘#’ 表示干地,‘.' 表示湿地,可以对图有两种操作:
- 1 x y 表示改变 (x,y) 这个位置的干湿
- 2 x y 表示查询 (x,y) 作为围墙的一部分的矩形最大面积是多少?
题解:
首先分析复杂度:T = 20,n = 1000,m = 1000,q = 1000, 所以每次操作最大复杂度是 n*log,考虑暴力维护每一个点上下左右最长的连续的干地,每次查询,求以(x,y) 这个点作为上下左右边界的最大值。
建一棵线段树,以(x,y) 这个点作为下边界为例,线段树每一个叶子节点存x这一行的每一个位置往上走的最大值,从 x 位置往上遍历,往上遍历的同时剔除线段树内不满足高度的节点,求每一行的合理区间最左边的1和最右边的1,这个合理区间表示的是遍历到的这一行 i ,位置 (i,y) 的左右区间和 (x,y) 的交集。注意最后判断得到的区间是都包含 y ,只有包含 y 的才是合法更新。
#include <bits/stdc++.h>
#define lson (id<<1)
#define rson (id<<1|1)
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e3+10;
typedef long long ll;
inline int read(){
int X=0; bool flag=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
if(flag) return X;
return ~(X-1);
}
char s[maxn];
int a[maxn][maxn];
int up[maxn][maxn],down[maxn][maxn];
int Left[maxn][maxn],Right[maxn][maxn];
int mins[maxn<<2],e[maxn];
void push_up(int id){
mins[id] = min(mins[lson],mins[rson]);
}
void build(int id,int l,int r){
if(l==r){
mins[id] = e[l];
return ;
}
int mid = (l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
push_up(id);
}
void update(int id,int l,int r,int val){
if(l==r){
mins[id] = inf;
return ;
}
int mid = (l+r)>>1;
if(mins[lson]<=val) update(lson,l,mid,val);
if(mins[rson]<=val) update(rson,mid+1,r,val);
push_up(id);
}
int queryL(int id,int l,int r,int x,int y){
if(r<l) return 2000;
if(l==r) return l;
int mid = (l+r)>>1,ans = 2000;
if(x<=mid&&mins[lson]<inf) ans = queryL(lson,l,mid,x,y);
if(y>mid&&mins[rson]<inf&&ans==2000) ans = queryL(rson,mid+1,r,x,y);
return ans;
}
int queryR(int id,int l,int r,int x,int y){
if(r<l) return -2000;
if(l==r) return l;
int mid = (l+r)>>1,ans = -2000;
if(y>mid&&mins[rson]<inf) ans = queryR(rson,mid+1,r,x,y);
if(x<=mid&&mins[lson]<inf&&ans==-2000) ans = queryR(lson,l,mid,x,y);
return ans;
}
int main(){
int T = read(),cas = 1;
while(T--){
int n = read(),m = read(),Q = read();
for(int i=1;i<=n;i++) {
scanf("%s",s+1);
for(int j=1;j<=m;j++){
if(s[j]=='#') a[i][j] = 1;
else a[i][j] = 0;
}
}
for(int i=1;i<=m;i++) up[0][i] = down[n+1][i] = 0;
for(int i=1;i<=n;i++) Left[i][0] = Right[i][m+1] = 0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]) Left[i][j] = Left[i][j-1]+1;
else Left[i][j] = 0;
}
for(int j=m;j>=1;j--){
if(a[i][j]) Right[i][j] = Right[i][j+1]+1;
else Right[i][j] = 0;
}
}
for(int j=1;j<=m;j++){
for(int i=1;i<=n;i++){
if(a[i][j]) up[i][j] = up[i-1][j] + 1;
else up[i][j] = 0;
}
for(int i=n;i>=1;i--){
if(a[i][j]) down[i][j] = down[i+1][j] + 1;
else down[i][j] = 0;
}
}
printf("Case #%d:
", cas++);
while(Q--){
int t = read(),x = read(),y = read();
if(t==1){
int pos = x,num = up[x][y];
if(!a[x][y]) num = -up[pos-1][y]-1;
up[pos][y] -=num,pos++;
while(pos<=n&&a[pos][y]) {
up[pos][y]-=num;
pos++;
}
pos = x,num = down[x][y];
if(!a[x][y]) num = -down[pos+1][y]-1;
down[pos][y]-=num,pos--;
while(pos>=1&&a[pos][y]){
down[pos][y]-=num;
pos--;
}
pos = y,num = Left[x][y];
if(!a[x][y]) num = -Left[x][pos-1]-1;
Left[x][pos]-=num,pos++;
while(pos<=m&&a[x][pos]){
Left[x][pos]-=num;
pos++;
}
pos = y,num = Right[x][y];
if(!a[x][y]) num = -Right[x][pos+1]-1;
Right[x][pos]-=num,pos--;
while(pos>=1&&a[x][pos]){
Right[x][pos]-=num;
pos--;
}
a[x][y] ^= 1;
}
else{
if(!a[x][y]){printf("0
");continue;}
int L = y-Left[x][y]+1,R = Right[x][y]+y-1,ans = R-L+1;
for(int i=1;i<=m;i++) e[i] = up[x][i];
build(1,1,m);
for(int i=1;i<x;i++){
if(!a[x-i][y]) continue;
update(1,1,m,i);
int el = max(L,y-Left[x-i][y]+1),er = min(R,Right[x-i][y]+y-1);
int lc = queryL(1,1,m,el,er),rc = queryR(1,1,m,el,er);
if(lc<=y&&y<=rc)ans = max(ans,(i+1)*(rc-lc+1));
}
for(int i=1;i<=m;i++) e[i] = down[x][i];
build(1,1,m);
for(int i=1;i<=n-x;i++){
if(!a[x+i][y]) continue;
update(1,1,m,i);
int el = max(L,y-Left[x+i][y]+1),er = min(R,Right[x+i][y]+y-1);
int lc = queryL(1,1,m,el,er),rc = queryR(1,1,m,el,er);
if(lc<=y&&y<=rc)ans = max(ans,(i+1)*(rc-lc+1));
}
int U = x - up[x][y] + 1,D = y + down[x][y] - 1;
ans = max(ans,D - U + 1);
for(int i=1;i<=n;i++) e[i] = Left[i][y];
build(1,1,n);
for(int i=1;i<y;i++){
if(!a[x][y-i]) continue;
update(1,1,n,i);
int eu = max(U,x - up[x][y - i] + 1),ed = min(D,down[x][y-i] + x - 1);
int lc = queryL(1,1,n,eu,ed),rc = queryR(1,1,n,eu,ed);
if(lc<=x&&x<=rc)ans = max(ans,(i+1)*(rc-lc+1));
}
for(int i=1;i<=n;i++) e[i] = Right[i][y];
build(1,1,n);
for(int i=1;i<=m-y;i++){
if(!a[x][y+i]) continue;
update(1,1,n,i);
int eu = max(U,x - up[x][y + i] + 1),ed = min(D,down[x][y+i] + x - 1);
int lc = queryL(1,1,n,eu,ed),rc = queryR(1,1,n,eu,ed);
if(lc<=x&&x<=rc)ans = max(ans,(i+1)*(rc-lc+1));
}
printf("%d
", ans);
}
}
}
return 0;
}
/*
10
4 3 3
###
#.#
#.#
###
2 3 2
1 3 2
2 3 2
*/