题解:CF31D Chocolate

bfs。

思路

总体来说:暴力删边,暴力搜索。

删边

首先不难发现:

(注意下面的图左上角的格子是 (1,1)(1,1),左上角的顶点是 (0,0)(0,0)

切掉一条横向边相当于把这条线上面的点和下面的点的连接切断了。

具体来说,我们假设这条线段的横坐标为 xx,纵坐标区间为 [l,r][l,r],那么对于任意正整数 i[l+1,r]i\in[l+1,r],都要切断 (x,i)(x,i)(x+1,i)(x+1,i) 的连接。

如果这条线段是纵向的,同理,设这条线段的纵坐标为 yy,横坐标区间为 [l,r][l,r],那么对于任意正整数 i[l+1,r]i\in[l+1,r],都要切断 (i,y)(i,y)(i,y+1)(i,y+1) 的连接。

那么问题转移到了如何实现删边上。

我们可以开一个数组 gg(后文的代码中为 go),其中 gi,j,kg_{i,j,k} 表示点 (i,j)(i,j) 向方向 kk 能否通行,11 代表不行,00 代表可以。k[1,4]k\in[1,4],当 kk 等于以下值是方向分别是:

  • k=1k=1,向右。
  • k=2k=2,向左。
  • k=3k=3,向上。
  • k=4k=4,向下。

有了这个数组之后,不难发现切断横向边就是假设这条线段的横坐标为 xx,纵坐标区间为 [l,r][l,r],那么对于任意正整数 i[l+1,r]i\in[l+1,r],都要使 gx,i,4=1g_{x,i,4}=1gx+1,i,3=1g_{x+1,i,3}=1。纵向同理。

然后暴力 bfs 即可。因为每个点至多遍历一次,所以是 O(n2)\operatorname{O}(n^2) 的。

#include<bits/stdc++.h>
using namespace std;
int n,m,q,go[505][505][5],vis[505][505],cnt,ans[250005],sum;
const int dx[5]={0,0,0,-1,1};
const int dy[5]={0,1,-1,0,0};
queue<int>q1,q2;
void bfs(int x,int y){
	q1.push(x);
	q2.push(y);
	vis[x][y]=1;
	while(!q1.empty()){
		x=q1.front();
		y=q2.front();
		sum++;
		q1.pop();
		q2.pop();
		for(int i=1;i<=4;i++){
			if(go[x][y][i])continue;
			int nx=x+dx[i],ny=y+dy[i];
			if(nx<=0||nx>n||ny<=0||ny>m)continue;
			if(vis[nx][ny])continue;
			vis[nx][ny]=1;
			q1.push(nx);
			q2.push(ny);
		}
	}
}
int main(){
	cin>>n>>m>>q;
	for(int i=1;i<=q;i++){
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		if(x1==x2){
			for(int j=y1+1;j<=y2;j++){
				go[x1][j][4]=1;
				go[x1+1][j][3]=1;
			}
		}
		else{
			for(int j=x1+1;j<=x2;j++){
				go[j][y1][1]=1;
				go[j][y1+1][2]=1;
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(!vis[i][j]){
				bfs(i,j);
				ans[++cnt]=sum;
				sum=0;
			}
		}
	}
	sort(ans+1,ans+cnt+1);
	for(int i=1;i<=cnt;i++)if(ans[i]!=0)cout<<ans[i]<<' ';
	return 0;
}
posted @   Weslie_qwq  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示