Problem A Artwork

题目链接:点击打开链接

 

A template for an artwork is a white grid of n × m squares. The artwork will be created by painting q horizontal and vertical black strokes. A stroke starts from square (x1, y1), ends at square (x2,y2)(x1=x2(x2,y2)(x1=x2 or y1=y2)y1=y2) and changes the color of all squares (x, y) to black where x1 ≤ x ≤ x2 and y1 ≤ y ≤ y2. The beauty of an artwork is the number of regions in the grid. Each region consists of one or more white squares that are connected to each other using a path of white squares in the grid, walking horizontally or vertically but not diagonally. The initial beauty of the artwork is 1. Your task is to calculate the beauty after each new stroke. Figure A.1 illustrates how the beauty of the artwork varies in Sample Input 1.

 

 

Input

The first line of input contains three integers n, m and q (1 ≤ n, m ≤ 1000, 1 ≤ q ≤ 104). Then follow q lines that describe the strokes. Each line consists of four integersx1,y1,x2x1,y1,x2 and y2(1 ≤ x1 ≤ x2 ≤ n, 1 ≤ y1 ≤ y2 ≤ m). Either x1 = x2 or y1=y2y1=y2(or both).

Output

For each of the q strokes, output a line containing the beauty of the artwork after the stroke.

Sample Input

4 6 5
2 2 2 6
1 3 4 3
2 5 3 5
4 6 4 6
1 6 4 6

Sample Output

1
3
3
4
3

 

题意:有一个n行m列的白色格子,现在对它采取k次涂黑操作,每次涂黑操作输入四个数,这四个数分别为起始点的x,y和终止点的x,y。要求是输出每次涂黑后,这个格子有多少个白色的联通块。

 

思路:反过来思考,首先将所有的涂黑操作全部涂完,使用一个结构体来记录所有的涂黑条,然后算出全部涂黑后有多少个联通块,这个是最终的联通块。

接下来对每个原先涂黑的一个个的涂白,就能依次求出倒数第一个第二个...整个图内所有的联通块。

注意:有些点涂了好几次。

#include<iostream>  
#include<cstdio>  
#include<algorithm>  
#include<cstdlib>  
#include<cstring>  
#define MAXN 5100000
using namespace std;  

struct Node{
	int numa; int numb; int numc; int numd;   
}node[11000];

int father[MAXN];  //父节点

struct HH{
	int to;  //所连接的第一个节点 
	int next;  //下一个与该节点连接的节点 
}arr[MAXN];

int ans[11000];  //答案 
int head[MAXN];  //所到达的第一个头部,会不断地更新 
int b[MAXN];  //置为1则为被抹黑 
bool visit[MAXN];   //置为1则为已经算过 
  
int m, n, k, x, y, z, sum;  
long long int ll;  

void init(int x, int y){  //构造一个节点,将x指向y 
	arr[++ll].to = y;  
	arr[ll].next = head[x];  
	head[x] = ll;    
} 

int get(int x){  //得到该节点的祖先节点 
	if(father[x] == -1) return x;  
	return father[x] = get(father[x]);   
}

void merge(int x, int y){  //合并两个节点所在的集合 
	int a = get(x);  
	int c = get(y);  
	if(a == c) return;  
	if(!visit[a] && !visit[c]) sum++; //两个这个时候都没算过
	if(visit[a] && visit[c]) sum--;  //反上 
	visit[a] = visit[c] =1;  
	father[a] = c;  
}

void Init(int x){  //将与x相连的所有点全部相连 
	for(int k=head[x]; k; k = arr[k].next){
		if(!b[arr[k].to]) merge(x,arr[k].to);  
	}
	if(!visit[x])sum++, visit[x] = 1;  
}

int main(){
	//freopen("1.txt", "r", stdin);    
	scanf("%d%d%d", &n,&m,&k);  
	for(int i=1;i<=(n+2)*(m+2);i++)  //父节点置-1 
		father[i] = -1;   
	for(int i=0;i<=m+1;i++)b[i] = 1;
	for(int i=(n+1)*(m+2); i<=(n+2)*(m+2); i++)b[i] = 1;
	for(int i=1;i<=n+2;i++){
		b[i*(m+2)-1] = 1;
	}
	for(int i=0;i<=n+2;i++){
		b[i*(m+2)] = 1;  
	}  
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			init(((m+2)*i)+j,((m+2)*i)+j+1);
			init(((m+2)*i)+j,((m+2)*i)+j-1);
			init(((m+2)*i)+j,((m+2)*(i+1)+j));
			init(((m+2)*i)+j,((m+2)*(i-1)+j));
		}
	}   
	for(int i=1; i<=k;i++){
		scanf("%d%d%d%d", &node[i].numa, &node[i].numb , &node[i].numc , &node[i].numd );  
		if(node[i].numa == node[i].numc ){
			for(int j=node[i].numb; j<=node[i].numd;j++){
				b[(m+2)*node[i].numa+j]++;  
			}
		}
		else {
			for(int j=node[i].numa;j<=node[i].numc;j++){
				b[(m+2)*j + node[i].numb]++;  
			}
		}
	}
	
	for(int i=0;i<(m+2)*(n+2);i++) {
		if(b[i] == 0) Init(i);  
	}
	
	ans[k+1] = sum;
	for(int i=k;i>=1;i--){  //对于每个划,逐个计算 
		if(node[i].numa == node[i].numc ){
			for(int j=node[i].numb; j<=node[i].numd;j++){
				b[(m+2)*node[i].numa+j]--; 
				if (!b[(m+2)*node[i].numa+j]) Init((m+2)*node[i].numa+j);
			}
		}
		else {
			for(int j=node[i].numa;j<=node[i].numc;j++){
				b[(m+2)*j + node[i].numb]--;
				if (!b[(m+2)*j + node[i].numb]) Init((m+2)*j + node[i].numb); 
			}
		}
		ans[i] = sum;  
	}
	

	for(int i=1;i<=k;i++){
		printf("%d\n", ans[i+1]);   
	}
}
 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值