[luogu5423]Valleys

先考虑不要求有洞,那么可以将所有权值排序,然后不断插入,那么一个连通块就是一个答案,加上连通块大小即可
考虑并查集如何判断是否有洞,可以发现对于任意一个无洞的直角多边形,都有$90度内角-90度外角=4$,而如果有洞显然就不是了(容易发现如果有k个洞,那么上述的值就应是$4-4k$)
这个角度看上去很难维护,其实只需要考虑包含他的四个2*2的矩形即可(或者说这个新点的四个较上的角的变化),分类讨论:
1.相邻的两个格子都没有,新增一个90度的内角
2.相邻的两个格子恰有一个,减少一个90度的内角
3.相邻的两个格子都有,且对角没有,减少两个90度的内角,新增一个90度的外角
4.相邻的两个格子都有,且对角也有,减少一个90度的外角
由此用并查集进行维护即可

 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 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/12238555.html