• [USACO21JAN] Paint by Letters P


    题意

    对于一个(n imes m)的矩形,每个方格都有一个颜色。
    令其价值为:相邻同色两点连边后,其连通块个数。
    例如

    AAB
    BBA
    BBB
    

    其价值为(4),四个连通块分别为

    ..B    AA.    ...    ...
    ...    ...    BB.    ..A
    ...    ...    BBB    ..
    
    

    给定一个(n imes m)的矩形,每个方格都有一个颜色,用大写字母表示,(q)次询问,每次给定(x_1,y_1,x_2,y_2(1le x_1le x_2le n,1le y_1le y_2le m)),输出这个矩形的价值。

    (n,m,qle 1000)

    做法

    相邻同色点连边后,这是一个平面图,而我们求的是连通块个数
    在题目中,矩形中的一个块是描述的一个颜色,为以下我们称为的
    在题目中,矩形中两个相邻块有公共边,但以下我们是间的边,要分清楚

    比如,样例

    ABBAAAAA
    ABAAAABA
    CAADABBA
    AAAAAAAA
    

    形成的平面图是这样样子的:

    欧拉公式:在平面图内,令(V)为点个数,(F)为面个数,(E)为边个数,(C)为连通块个数,有(F+V-E=C+1)

    证明:
    考虑特殊情况,连通块个数为(1)的情况,若我们能证明(F+V-E=2),那么多个连通块也同理。
    选出该连通块任意生成树,考虑该平面图的对偶图,对偶图两个点存在连边,当且仅当该连边不穿过生成树。
    考虑我们得到的对偶图,一定是无环的,若有环,则必定将某两点分割开了,显然与之前得到的生成树矛盾,那么也容易得到这个对偶图是连通的。
    生成树的点数为(V),对偶图的点数为(F),容易知道其边数和为(E),有树的定义我们得到(V+F=E+2)
    得证。

    有欧拉公式得到(V=V+F-E-1),那么转化为求(V,F,E)
    (V)即为((x_2-x_1+1)(y_2-y_1+1)),边可以通过(O(nm))预处理,通过前缀和(O(1))计算。

    现在难点在于求面数(F)
    考虑一个完整的矩形,如何求面数?
    ([i,j])为以点((i,j))为左上角的方格,以([i,j])为位置,然后进行遍历,找到围住这个方格可能的四条边,判断哪条边不存在,若不存在则可前往那个点。注意若([i,j])可以走出这个矩形,说明是外面的那个无界面。

    我们对于这个完整的矩形,每个面(除无界面),任选一个方格记为关键点,对于查询,查询内部的关键点个数(也可以通过前缀和优化)。
    此时遇到一个问题,一个面,在完整的矩形内是有界面,但在这个子矩形中是无界面,如何解决呢?
    我们遍历外面这圈红色的方格,查看其关键点是否被统计过(是否关键点包含在子矩形内部),因为这圈红色的相对于这个子矩阵是无界面,然后减去

    code

    如果不理解可以看下代码,下面的代码未统计无界面,故在统计答案的时候,(C=V+F-E)

    #include<bits/stdc++.h>
    typedef int LL;
    typedef double dl;
    #define opt operator
    #define pb push_back
    #define pii std::pair<LL,LL>
    const LL maxn=1e3+9,mod=998244353,inf=0x3f3f3f3f;
    LL Read(){
    	LL x(0),f(1); char c=getchar();
    	while(c<'0' || c>'9'){
    		if(c=='-') f=-1; c=getchar();
    	}
    	while(c>='0' && c<='9'){
    		x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
    	}return x*f;
    }
    void Chkmin(LL &x,LL y){
    	if(y<x) x=y;
    }
    void Chkmax(LL &x,LL y){
    	if(y>x) x=y;
    }
    LL add(LL x,LL y){
    	return x+=y,x>=mod?x-mod:x;
    }
    LL dec(LL x,LL y){
    	return x-=y,x<0?x+mod:x;
    }
    LL mul(LL x,LL y){
    	return 1ll*x*y%mod;
    }
    LL Pow(LL base,LL b){
    	LL ret(1); while(b){
    		if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
    	}return ret;
    }
    const LL dx[]={0,-1,0,1,0},dy[]={0,0,1,0,-1};
    LL n,m,key,q;
    LL bel[maxn][maxn],fk[maxn][maxn],fe[2][maxn][maxn],mark[maxn][maxn];
    LL vis[maxn*maxn<<1];
    pii pos[maxn*maxn<<1];
    char s[maxn][maxn];
    void Bfs(LL sx,LL sy){
    	std::queue<pii> que;
    	que.push(pii(sx,sy));
    	fk[sx][sy]=1;
    	mark[sx][sy]=1;
    	++key; pos[++key]=pii(sx,sy);
    	bel[sx][sy]=key;
    	LL flag(0);
    	while(que.size()){
    		LL x(que.front().first),y(que.front().second);
    		que.pop();
    		for(LL i=1;i<=4;++i){
    			LL xx(x+dx[i]),yy(y+dy[i]);
    			if(x==xx){
    				if((s[x][std::max(y,yy)]==s[x+1][std::max(y,yy)])) continue;
    			}else{
    				assert(y==yy);
    				if((s[std::max(x,xx)][y]==s[std::max(x,xx)][y+1])) continue;
    			}
    			if(xx<1 || xx>n || yy<1 || yy>m){
    				flag=1;
    				continue;
    			}
    			if(!mark[xx][yy]){
    				bel[xx][yy]=key;
    				mark[xx][yy]=1;
    				que.push(pii(xx,yy));
    			}
    		}
    	}
    	if(flag){
    		fk[sx][sy]=0;
    		pos[key]=pii(0,0);
    	}
    }
    void Init(){
    	for(LL i=1;i<n;++i){
    		for(LL j=1;j<m;++j){
    			if(!mark[i][j]){
    				Bfs(i,j);
    			}
    		}
    	}
    	for(LL i=1;i<=n;++i){
    		for(LL j=2;j<=m;++j){
    			fe[0][i][j]=s[i][j]==s[i][j-1];
    		}
    	}
    	for(LL i=2;i<=n;++i){
    		for(LL j=1;j<=m;++j){
    			fe[1][i][j]=s[i][j]==s[i-1][j];
    		}
    	}
    	for(LL i=1;i<=n;++i){
    		for(LL j=1;j<=m;++j){
    			for(LL k=0;k<2;++k){
    				fe[k][i][j]+=fe[k][i-1][j]+fe[k][i][j-1]-fe[k][i-1][j-1];
    			}
    			fk[i][j]+=fk[i-1][j]+fk[i][j-1]-fk[i-1][j-1];
    		}
    	}
    }
    LL Getsum(LL sum[][maxn],LL lx,LL ly,LL rx,LL ry){
    	if(lx>rx || ly>ry) return 0;
    	return sum[rx][ry]-sum[lx-1][ry]-sum[rx][ly-1]+sum[lx-1][ly-1];
    }
    LL In(LL nw,LL lx,LL ly,LL rx,LL ry){
    	if(lx>rx || ly>ry) return 0;
    	LL x(pos[nw].first),y(pos[nw].second);
    	return lx<=x && x<=rx && ly<=y && y<=ry;
    }
    int main(){
    	n=Read(); m=Read(); q=Read();
    	for(LL i=1;i<=n;++i){
    		scanf(" %s",s[i]+1);
    	}
    	Init();
    	while(q--){
    		LL lx(Read()),ly(Read()),rx(Read()),ry(Read());
    		LL V((rx-lx+1)*(ry-ly+1));
    		LL E(Getsum(fe[0],lx,ly+1,rx,ry)+Getsum(fe[1],lx+1,ly,rx,ry));
    		LL F(Getsum(fk,lx,ly,rx-1,ry-1));
    		for(LL i=lx;i<=rx;++i){
    			if(!vis[bel[i][ly-1]] && In(bel[i][ly-1],lx,ly,rx-1,ry-1)){
    				--F; vis[bel[i][ly-1]]=1;
    			}
    			if(!vis[bel[i][ry]] && In(bel[i][ry],lx,ly,rx-1,ry-1)){
    				--F; vis[bel[i][ry]]=1;
    			}
    		}
    		for(LL i=ly;i<=ry;++i){
    			if(!vis[bel[lx-1][i]] && In(bel[lx-1][i],lx,ly,rx-1,ry-1)){
    				--F; vis[bel[lx-1][i]]=1;
    			}
    			if(!vis[bel[rx][i]] && In(bel[rx][i],lx,ly,rx-1,ry-1)){
    				--F; vis[bel[rx][i]]=1;
    			}
    		}
    		printf("%d
    ",V+F-E);
    		for(LL i=lx;i<=rx;++i){
    			vis[bel[i][ly-1]]=vis[bel[i][ry]]=0;
    		}
    		for(LL i=ly;i<=ry;++i){
    			vis[bel[lx-1][i]]=vis[bel[rx][i]]=0;
    		}
    	}
    	return 0;
    }
    `
  • 相关阅读:
    从“窃听门”事件解读手机Rootkit攻击
    一款好用的开源信息安全管理系统演示(视频)
    P1908-逆序对
    P1010-幂次方
    P1226-快速幂
    P1433-吃奶酪
    ACM模板——玄学逐字符输入输出
    P1434-滑雪
    P1118-数字三角形
    P1443-马的遍历
  • 原文地址:https://www.cnblogs.com/Grice/p/14467116.html
Copyright © 2020-2023  润新知