此题是并查集。考虑到水位不断上涨,所以将时间倒转。先统计最后一天的联通块个数,每一天浮出水面的块进行计算。复杂度O(玄学)。
代码如下
#include<cstdio> #include<cctype> #include<algorithm> using namespace std; short u[5]={0,0,1,0,-1}; short w[5]={0,1,0,-1,0}; int n,m; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch==‘-‘)f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-‘0‘; ch=getchar(); } return num*f; } struct Point{ short x,y; int h; bool operator <(const Point &a)const{ return h } }mp[10000000]; int num; int Map[3001][3001]; int que[1000000]; int ans[1000000]; int father[10000000]; bool jd[3001][3001]; int find(int x){ if(father[x]!=x)father[x]=find(father[x]); return father[x]; } inline void Union(int x,int y){ x=find(x);y=find(y); father[y]=x; } inline int query(int i,int j){ return (i-1)*m+j; } int main(){ n=read(),m=read(); for(int i=1;i<=n*m+1;++i) father[i]=i; int s; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ s=read(); mp[++num]=(Point){i,j,s}; Map[i][j]=s; } sort(mp+1,mp+num+1); int T=read(); for(int i=1;i<=T;++i) que[i]=read(); int cnt=1; for(int i=T;i>=1;--i){ ans[i]=ans[i+1]; while(mp[cnt].h>que[i]&&cnt<=n*m){ jd[mp[cnt].x][mp[cnt].y]=1; ans[i]++; for(int j=1;j<=4;++j){ short x=mp[cnt].x+u[j]; short y=mp[cnt].y+w[j]; if(x>0&&y>0&&x<=n&&y<=m&&Map[x][y]>que[i]&&jd[x][y]&&find(query(x,y))!=find(query(mp[cnt].x,mp[cnt].y))){ Union(query(x,y),query(mp[cnt].x,mp[cnt].y)); ans[i]--; } } cnt++; } } for(int i=1;i<=T;++i)printf("%d ",ans[i]); return 0; }
时间: 2024-10-03 02:03:06