• P7295-[USACO21JAN]Paint by Letters P【平面图欧拉公式】


    正题

    题目链接:https://www.luogu.com.cn/problem/P7295


    题目大意

    给出(n*m)的网格,每个格子上有字母,相同字母的四联通相邻格子为连通,每次询问一个子矩阵求连通块个数。

    (1leq n,m,qleq 1000)


    解题思路

    首先一张连通的平面图有欧拉公式

    [V+F=E+2 ]

    其中(V,E,F)分别表示点数,边数,区域个数(对偶图点数)。

    然后不连通的对偶图会共用一个无界域,设为(C)个连通块,无界域会重复统计(C-1)次,然后联立得

    [V+F-E=C+1 ]

    然后考虑怎么用这个求,首先是(V,E),这个很容易搞,(V)直接计算,(E)用二维前缀和算就好了。

    主要是(F)怎么搞,先构出不严格的对偶图(就是每个格子边上的点当做点),然后(F)就是对偶图的连通块数。

    对于整张图的每个连通块,我们选择任意一个点标记,然后记录每个点对应连通块的标记点,然后直接二维前缀和统计连通块内的标记点个数,然后枚举边界减去边上不完整被统计的的连通块最后加上无界域就好了。

    时间复杂度(O(nm+q(n+m)))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=1100;
    const int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
    int n,m,q,E[2][N][N],mx[N][N],my[N][N],F[N][N];
    char s[N][N];bool v[N][N];
    bool edg(int x,int y,int zx,int zy){
    	if(zx<0||zy<0||zx>n||zy>m)return 0;
    	if(zx>0&&zx<n&&zy==y+1)return (s[x][y+1]!=s[x+1][y+1]);
    	if(zx>0&&zx<n&&zy==y-1)return (s[x][y]!=s[x+1][y]);
    	if(zy>0&&zy<m&&zx==x+1)return (s[x+1][y]!=s[x+1][y+1]);
    	if(zy>0&&zy<m&&zx==x-1)return (s[x][y]!=s[x][y+1]);
    	return 1;
    }
    void dfs(int x,int y){
    	if(x==1&&y==4)
    		x++,x--;
    	if(v[x][y])return;v[x][y]=1;
    	for(int k=0;k<4;k++){
    		int zx=x+dx[k],zy=y+dy[k];
    		if(edg(x,y,zx,zy)){
    			mx[zx][zy]=mx[x][y];
    			my[zx][zy]=my[x][y];
    			dfs(zx,zy);
    		}
    	}
    	return;
    }
    #define Get(F,x1,y1,x2,y2) (F[x2][y2]-((x1)?F[x1-1][y2]:0)-((y1)?F[x2][y1-1]:0)+(((x1)&&(y1))?F[x1-1][y1-1]:0))
    int check(int x,int y,int x1,int y1,int x2,int y2){
    	if(!v[x][y]&&x>=x1&&x<=x2&&y>=y1&&y<=y2)
    	{v[x][y]=1;return 1;}
    	return 0;
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&q);
    	for(int i=1;i<=n;i++)
    		scanf("%s",s[i]+1);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			E[0][i][j]=E[0][i-1][j]+E[0][i][j-1]-E[0][i-1][j-1];
    			E[1][i][j]=E[1][i-1][j]+E[1][i][j-1]-E[1][i-1][j-1];
    			if(s[i][j]==s[i][j+1])E[0][i][j]++;
    			if(s[i][j]==s[i+1][j])E[1][i][j]++;
    		}
    	for(int i=0;i<=n;i++)
    		for(int j=0;j<=m;j++)
    			if(!v[i][j]){
    				mx[i][j]=i;my[i][j]=j;
    				F[i][j]++;dfs(i,j);
    			}
    	for(int i=0;i<=n;i++)
    		for(int j=0;j<=m;j++)
    			F[i][j]+=(i?F[i-1][j]:0)+(j?F[i][j-1]:0)-((i&&j)?F[i-1][j-1]:0);
    	memset(v,0,sizeof(v));
    	while(q--){
    		int x1,y1,x2,y2,ans=0;
    		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    		ans+=(x2-x1+1)*(y2-y1+1);
    		if(y1!=y2)ans-=Get(E[0],x1,y1,x2,y2-1);
    		if(x1!=x2)ans-=Get(E[1],x1,y1,x2-1,y2);
    		x1--;y1--;ans+=Get(F,x1,y1,x2,y2);
    		for(int i=x1;i<=x2;i++){
    			ans-=check(mx[i][y1],my[i][y1],x1,y1,x2,y2);
    			ans-=check(mx[i][y2],my[i][y2],x1,y1,x2,y2);
    		}
    		for(int i=y1;i<=y2;i++){
    			ans-=check(mx[x1][i],my[x1][i],x1,y1,x2,y2);
    			ans-=check(mx[x2][i],my[x2][i],x1,y1,x2,y2);
    		}
    		printf("%d
    ",ans);
    		for(int i=x1;i<=x2;i++){
    			v[mx[i][y1]][my[i][y1]]=0;
    			v[mx[i][y2]][my[i][y2]]=0;
    		}
    		for(int i=y1;i<=y2;i++){
    			v[mx[x1][i]][my[x1][i]]=0;
    			v[mx[x2][i]][my[x2][i]]=0;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    yum、RPM常用的命令(转)
    10个你可能从未用过的PHP函数(转)
    sphinx 增量索引 及时更新、sphinx indexer索引合成时去旧和过滤办法(转)
    MySQL查询in操作 查询结果按in集合顺序显示(转)
    sphinx配置文件继承
    开源搜索引擎Sphinx 中启动多个搜索进程的方法(转)
    sphinx的简单实例
    Python中的join()函数的用法
    django queryset values&values_list
    Django中的QuerySet查询优化之select_related
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15152748.html
Copyright © 2020-2023  润新知