“ 对于T次讯问,倒叙考察之。 则问题变成了水面不断下降的情况 。这个时候,每一单位时间,会有额外的格子露出水平,并有可能合并原有的不相连的区域。这样的问题可以被描述为 对若干集合进行合并 ,用 并查集 是最高效的处理方案。总的时间复杂度为O(TlogT+nm)。 ”
细节一下 ,可知,倒叙操作时,假如新出现的小方格周围全是海洋,那么它就能提供一个答案值(当成一个小块)。
接着,我们易知, 每次的方格更新最多只会联通它四个方向上的块 ,所以我们 不需要对整个方格整体进行扫描 ,只需要让当前格 与四个方向上块依次进行合并(还没出现的块不合并) 。当发现他们在同一个块上时,不操作。但他们分别属于不同块时,将其合并,并将答案值减一即可。
# include <stdio.h>
# include <algorithm>
using namespace std;
const int dx[]={1,-1,0,0},dy[]={0,0,-1,1};
struct block {
int x,y,h;
}c[1000100];
int k,q[100100];
int n,m,fa[1000100],b[100100];
bool up[1010][1010];
inline int getf(int x) {
int r=x;
while(fa[r]!=r) r=fa[r];
int i=x,j;
while(i!=r) {
j=fa[i];
fa[i]=r;
i=j;
}
return r;
}
inline int cmp_block(block _x,block _y) {return _x.h>_y.h;}
int main() {
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j) {
int id=(i-1)*m+j;
c[id].x=i,c[id].y=j;
fa[id]=id;
scanf("%d",&c[id].h);
}
sort(c+1,c+n*m+1,cmp_block);
scanf("%d",&k);
for (int i=1;i<=k;++i)
scanf("%d",&q[i]);
int now=1,ans=0;
for (int i=k;i>=1;--i) {
while(now<=n*m && c[now].h>q[i]) {
ans++; int nowx=c[now].x,nowy=c[now].y;
up[nowx][nowy]=1;
for (int j=0;j<4;++j) {
int xx=nowx+dx[j],yy=nowy+dy[j];
if(xx<1||xx>n||yy<1||yy>m|| !up[xx][yy]) continue;
int id1=(xx-1)*m+yy,id2=(nowx-1)*m+nowy,fid1=getf(id1),fid2=getf(id2);
if(fid1!=fid2) {
fa[fid1]=fid2;
ans--;
}
}
now ++;
}
b[i]=ans;
}
for(int i=1;i<k;++i) printf("%d ",b[i]);
printf("%d\n",b[k]);
return 0;
}