[luogu5423]Valleys
先考虑不要求有洞,那么可以将所有权值排序,然后不断插入,那么一个连通块就是一个答案,加上连通块大小即可
考虑并查集如何判断是否有洞,可以发现对于任意一个无洞的直角多边形,都有$90度内角-90度外角=4$,而如果有洞显然就不是了(容易发现如果有k个洞,那么上述的值就应是$4-4k$)
这个角度看上去很难维护,其实只需要考虑包含他的四个2*2的矩形即可(或者说这个新点的四个较上的角的变化),分类讨论:
1.相邻的两个格子都没有,新增一个90度的内角
2.相邻的两个格子恰有一个,减少一个90度的内角
3.相邻的两个格子都有,且对角没有,减少两个90度的内角,新增一个90度的外角
4.相邻的两个格子都有,且对角也有,减少一个90度的外角
由此用并查集进行维护即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1000005 4 struct ji{ 5 int x,y,k; 6 }a[N]; 7 int n,f[N],sz[N],sum[N],vis[1005][1005],dx[4]={-1,0,0,1},dy[4]={0,-1,1,0}; 8 long long ans; 9 bool cmp(ji x,ji y){ 10 return x.k<y.k; 11 } 12 int id(int x,int y){ 13 return (x-1)*n+y; 14 } 15 int find(int k){ 16 if (k==f[k])return k; 17 return f[k]=find(f[k]); 18 } 19 void add(int x,int y){ 20 sz[id(x,y)]=vis[x][y]=1; 21 for(int i=0;i<4;i++){ 22 int tx=x+dx[i],ty=y+dy[i]; 23 if (vis[tx][ty]){ 24 if (find(id(tx,ty))==id(x,y))continue; 25 sz[id(x,y)]+=sz[find(id(tx,ty))]; 26 sum[id(x,y)]+=sum[find(id(tx,ty))]; 27 f[find(id(tx,ty))]=id(x,y); 28 } 29 } 30 for(int i=-1;i<2;i+=2) 31 for(int j=-1;j<2;j+=2){ 32 int tx=x+i,ty=y+j,s=vis[tx][y]+vis[x][ty]; 33 if (!s)sum[id(x,y)]++; 34 if (s==1)sum[id(x,y)]--; 35 if (s==2){ 36 if (vis[tx][ty])sum[id(x,y)]++; 37 else sum[id(x,y)]-=3; 38 } 39 } 40 if (sum[id(x,y)]==4)ans+=sz[id(x,y)]; 41 } 42 int main(){ 43 scanf("%d",&n); 44 for(int i=1;i<=n;i++) 45 for(int j=1;j<=n;j++){ 46 scanf("%d",&a[id(i,j)].k); 47 a[id(i,j)].x=i; 48 a[id(i,j)].y=j; 49 f[id(i,j)]=id(i,j); 50 } 51 sort(a+1,a+n*n+1,cmp); 52 for(int i=1;i<=n*n;i++)add(a[i].x,a[i].y); 53 printf("%lld",ans); 54 }