zoukankan      html  css  js  c++  java
  • [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;
    }
    `
  • 相关阅读:
    nowcoderD Xieldy And His Password
    Codeforces681D Gifts by the List
    nowcoder80D applese的生日
    Codeforces961E Tufurama
    Codeforces957 Mahmoud and Ehab and yet another xor task
    nowcoder82E 无向图中的最短距离
    nowcoder82B 区间的连续段
    Codeforces903E Swapping Characters
    Codeforces614C Peter and Snow Blower
    Codeforces614D Skills
  • 原文地址:https://www.cnblogs.com/Grice/p/14467116.html
Copyright © 2011-2022 走看看