题目链接:点击进入
题目
题意
n * m 的网格,每秒删除其中一个小网格,问每秒后,还剩余多少个好的矩形 ( 只要是矩形,同时矩形内没有被删除的格子 )
思路
蚌埠住啦,这题因为万恶的javaweb被我搁置了好久,我老早就想写了,难受≧ ﹏ ≦
按照顺序删除点,对于每个删除的点,看会影响多少新矩形。思考一下 ( 脑袋里构一下图 ) ,一个新删除的点,能影响到的,只能是以这个点 x 为中轴,上下每行左右扩到最大的部分 ( 最大就是指碰到被删除的点,或者边界 ) ,这个上下每行也是在x轴上上下扩到最大的。我们若是确定每行能扩到最大的边界,那么我们就可以通过枚举上下边界 ( 可以外枚举下边界,确定下边界后,内枚举上边界 ) ,两者取公共部分形成矩形,由于枚举的下边界是确定的,那么内枚举的上边界,每次 + 1 ,都会产生新的被影响的好矩形,被影响的数量就是 ( x - l + 1 ) * ( r - x + 1 ) ( l , r 就是此时上下边界形成公共部分的左边 l 与右边r )( 这个大家手画一下可能好理解一点 ) 。
求完这个点会影响的好的矩形的数量,就在看这个点会对后面的点产生的影响。
这个影响无非就是,对后面的点在求每行能扩的最大边界的产生影响
这里用
lmax [ xi ] [ yi ] 记录位置 ( xi , yi ) 向左能扩的最近点的 y 值
rmin [ xi ] [ yi ] 记录位置 ( xi , yi ) 向右能扩的最近点的 y 值
( 这里 x y 放置是以二维数组内的模式放置的,而不是二维坐标 )
每次一个点 ( x0 , y0 ) 删除过后,会影响 x = x0 , y < y0 的点的 rmin 值,会影响 x = x0 , y > y0 的点的 lmax 值。当然这个也不是所有满足 x = x0 的点都会被影响,这里被影响的只会是点 ( x0 , y0 ) 左右能扩到的最大范围的点。
代码
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=510;
int n,m,lmax[maxn][maxn],rmin[maxn][maxn];
bool vis[maxn][maxn];
int main( )
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
lmax[i][j]=0,rmin[i][j]=m+1;
ll ans=1LL*n*(n+1)*m*(m+1)/4;
for(int i=1;i<=n*m;i++)
{
int x,y;
cin>>x>>y;
int lowl=0,lowr=m+1;
for(int low=x;low>=1&&!vis[low][y];low--)
{
lowl=max(lowl,lmax[low][y]+1);
lowr=min(lowr,rmin[low][y]-1);
int highl=lowl,highr=lowr;
for(int high=x;high<=n&&!vis[high][y];high++)
{
highl=max(highl,lmax[high][y]+1);
highr=min(highr,rmin[high][y]-1);
ans=ans-1LL*(highr-y+1)*(y-highl+1);
}
}
cout<<ans<<endl;
for(int i=y-1;i>=1&&!vis[x][i];i--) rmin[x][i]=y;
for(int i=y+1;i<=m&&!vis[x][i];i++) lmax[x][i]=y;
vis[x][y]=1;
}
return 0;
}