• 6525. 【2020.4.1模拟】Valleys


    题目

    给你一个地图,每个格子上的数表示其高度。
    “山谷”的定义是:某个周围高度都大于它的、并且没有洞的块。
    有洞的定义是:将块删了之后,剩余的鸽子不能通过点相交形成一个连通块。
    求所有山谷的大小之和。


    正解

    思考历程就没有了,一开始根本就没有好好地思考这道题。

    先考虑如果没有洞该怎么做。这就是个小学生题:用并查集来维护块。将高度从小到大排序,对一个点进行操作时,将它相邻的、高度小于等于它点和它所在块合并。顺便记一下点数,就可以求出答案了。

    现在我们的问题是,如何快速地判断某个块是否有洞。
    方法似乎很多,这里就介绍一下平面图欧拉公式:
    (V)为点数,(E)为边数,(F)为区域数,则(V+F=E+2)

    考虑一个合法的山谷。首先计算一下有多少个小正方形(块内边长为(1)的互相连通的点形成的正方形),记为(S)
    如果这个山谷合法,那么(F=S+1)。后面的这个(1)是最外面的那个块。
    如果不合法,这条等式就不成立了。

    于是我们只需维护(V)(E)(S)。对于(S)的维护,注意到新生成的小正方形的格点肯定包含当前点,枚举一下就可以方便地维护。


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 800
    #define ll long long
    int n,m;
    int id[N][N],h[N*N];
    bool vis[N*N];
    #define at(x,y) h[id[x][y]]
    struct DOT{int x,y;} d[N*N];
    inline bool cmpd(DOT a,DOT b){return at(a.x,a.y)<at(b.x,b.y);}
    const int dx[4]={1,0,-1,0};
    const int dy[4]={0,1,0,-1};
    int fa[N*N],V[N*N],E[N*N],sq[N*N];
    int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
    #define in(x,y,z) ((1<=x && x<=n && 1<=y && y<=n) && getfa(id[x][y])==z)
    int main(){
    //	freopen("in.txt","r",stdin);
    	freopen("valleys.in","r",stdin);
    	freopen("valleys.out","w",stdout);
    	scanf("%d",&n);
    	for (int i=1;i<=n;++i)
    		for (int j=1;j<=n;++j){
    			id[i][j]=++m;
    			d[m]={i,j};
    			scanf("%d",&h[m]);
    		}
    	sort(d+1,d+m+1,cmpd);
    	for (int i=1;i<=m;++i)
    		fa[i]=i,V[i]=1,E[i]=0,sq[i]=0;
    	ll ans=0;
    	for (int i=1;i<=m;++i){
    		int x=d[i].x,y=d[i].y,z=id[x][y];
    		vis[z]=1;
    		for (int j=0;j<4;++j){
    			int tx=x+dx[j],ty=y+dy[j],tz=id[tx][ty];
    			if (tx<1 || tx>n || ty<1 || ty>n || !vis[tz])
    				continue;
    			int r=getfa(tz);
    			if (r!=z){
    				fa[r]=z;
    				V[z]+=V[r];
    				E[z]+=E[r];
    				sq[z]+=sq[r];
    			}
    			E[z]++;
    		}
    		for (int j=0;j<4;++j){
    			int k=(j+1)%4;
    			if (in(x+dx[j],y+dy[j],z) && in(x+dx[k],y+dy[k],z) && in(x+dx[j]+dx[k],y+dy[j]+dy[k],z))
    				sq[z]++;
    		}
    		if (i==m || at(x,y)!=at(d[i+1].x,d[i+1].y))
    			ans+=(E[z]-V[z]+2-sq[z]==1?V[z]:0);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    总结

    那些奇奇怪怪的公式定理,真该积累一些呢……

  • 相关阅读:
    《大话数据结构》最小生成树——Prim算法
    《大话数据结构》图的BFS和DFS
    寒假集训日志(三)——数论
    寒假集训日志(二)——最小生成树,拓扑排序,欧拉回路,连通路
    set
    寒假集训日志(一)——图,最短路问题
    经典的图论算法,C++描述
    动态数组
    stack and queue
    最长递增子序列,最长公共子串,最长公共子序列问题,最长公共增长子序列问题
  • 原文地址:https://www.cnblogs.com/jz-597/p/12623307.html
Copyright © 2020-2023  润新知