poj 3467 Cross Counting

题意:给出一个M*N 的矩阵,矩阵中每个元素都有一种颜色(1 ≤ N, M, C ≤ 100),10000条查询,更改某个元素的颜色或查询某种颜色的corss有多少个。

We say there exists a cross of size k centered at the cell (x,y)iff all cells lying in the x-th row or the y-th column and within a distance of k from (x,y) share the same color. if two crosses have the same center but different sizes we consider they are distinct

解法:由于每种颜色之间互不相关,因此可以分颜色考虑,将矩阵变为100个01矩阵,这样可以统计出初始时每种颜色有多少个cross。

当某个元素(i,j)发生改变时,只影响i行或j列为中心的cross,因此只需逐个考虑每个中心的cross的变化,每次查询只需更改200的元素的cross数目。怎么快速统计出以每个元素为中心的cross数目呢?根据定义,只需记录01矩阵中每个元素向上下左右四个方向连续1的个数,取最小值即可,当一个元素从1变为0或从0变为1,只需改变同行元素的左右方向连续1的个数和同列元素的上下方向连续1的个数,并记录cross数目增加减少的数量即可。

貌似线段树也可以维护,没想到这么维护,所以就暂且认为复杂度比这个高了(囧)。。。

import java.util.Scanner;

public class CountCross3467 {
	int maxn = 110;
	class Matrix {
		int map[][] = new int[maxn][maxn];
		int left[][] = new int[maxn][maxn], right[][] = new int[maxn][maxn],
				up[][] = new int[maxn][maxn], down[][] = new int[maxn][maxn];
		int sum, n, m;
        Matrix(int n,int m){
        	this.n=n;
        	this.m=m;
        }
		void init(int i, int j) {
			map[i][j] = 1;
			left[i][j] = right[i][j] = j;
			up[i][j] = down[i][j] = i;
		}
		void count() {
			for (int i = 1; i <= n; i++) {
				for (int j = 1; j <= m; j++)
					if (map[i][j] == 1 && map[i][j - 1] == 1)
						left[i][j] = left[i][j - 1];					
				for (int j = m; j > 0; j--)
					if (map[i][j] == 1 && map[i][j + 1] == 1)
						right[i][j] = right[i][j + 1];	
			}
			
			for (int i = 1; i <= m; i++) {
				for (int j = 1; j <= n; j++)
					if (map[j][i] == 1 && map[j - 1][i] == 1)
						up[j][i] = up[j - 1][i];
				for (int j = n; j > 0; j--)
					if (map[j][i] == 1 && map[j + 1][i] == 1)
						down[j][i] = down[j + 1][i];
			}
			
			sum = 0;
			for (int i = 1; i <= n; i++)
				for (int j = 1; j <= m; j++) 
					sum += cross(i, j);				
		}
		int cross(int i, int j) {
			if(map[i][j]==0)
				return 0;
			int a = Math.min(i - up[i][j], down[i][j] - i);
			int b = Math.min(j-left[i][j], right[i][j] - j);
			return Math.min(a, b);
		}
		void update1(int a, int b) {
			// 0-->1
			map[a][b] = 1;
			left[a][b] = b;
			if (map[a][b - 1] == 1)
				left[a][b] = left[a][b - 1];
			right[a][b] = b;
			if (map[a][b + 1] == 1)
				right[a][b] = right[a][b + 1];
			up[a][b] = a;
			if (map[a - 1][b] == 1)
				up[a][b] = up[a - 1][b];
			down[a][b] = a;
			if (map[a + 1][b] == 1)
				down[a][b] = down[a + 1][b];
			sum+=cross(a,b);
			for (int i = 1; i < b; i++) {
				if (right[a][i] != b - 1)
					continue;
				int temp = cross(a, i);
				right[a][i] = right[a][b];
				sum += cross(a, i) - temp;
			}
			for (int i = b + 1; i <= m; i++) {
				if (left[a][i] != b + 1)
					continue;
				int temp = cross(a, i);
				left[a][i] = left[a][b];
				sum += cross(a, i) - temp;
			}
			for(int i=1;i<a;i++){
				if(down[i][b]!=a-1)
					continue;
				int temp=cross(i,b);
				down[i][b]=down[a][b];
				sum += cross(i,b) - temp;
			}
			for(int i=a+1;i<=n;i++){
				if(up[i][b]!=a+1)
					continue;
				int temp=cross(i,b);
				up[i][b]=up[a][b];
				sum += cross(i,b) - temp;
			}
		}
		void update0(int a, int b) {
			// 1-->0
			sum-=cross(a,b);
			map[a][b] = 0;
			left[a][b] =right[a][b]=up[a][b]=down[a][b]=0;
			for (int i = 1; i < b; i++) {
				if (map[a][i]==0||right[a][i]<b)
					continue;
				int temp = cross(a, i);
				right[a][i] =b-1;
				sum -= temp-cross(a, i);
			}
			for (int i = b + 1; i <= m; i++) {
				if (map[a][i]==0||left[a][i]>b)
					continue;
				int temp = cross(a, i);
				left[a][i] = b+1;
				sum -= temp-cross(a, i);
			}
			for(int i=1;i<a;i++){
				if(map[i][b]==0||down[i][b]<a)
					continue;
				int temp=cross(i,b);
				down[i][b]=a-1;
				sum-= temp-cross(i,b);
			}
			for(int i=a+1;i<=n;i++){
				if(map[i][b]==0||up[i][b]>a)
					continue;
				int temp=cross(i,b);
				up[i][b]=a+1;
				sum -= temp-cross(i,b);
			}
		}
	}
	Scanner scan=new Scanner(System.in);

	void run(){
		int n=scan.nextInt();
		int m=scan.nextInt();
		int c=scan.nextInt();
		int q=scan.nextInt();
		int map[][]=new int[n+1][m+1];
		Matrix mat[]=new Matrix[c+1];
		for(int i=1;i<=c;i++)
			mat[i]=new Matrix(n,m);
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++){
				map[i][j]=scan.nextInt();
				mat[map[i][j]].init(i, j);
			}
		for(int i=1;i<=c;i++)
			mat[i].count();
		
		while(q-->0){
			String s=scan.next();
			if(s.charAt(0)=='Q'){
				int v=scan.nextInt();
				System.out.println(mat[v].sum);
			}
			else{
				int i=scan.nextInt();
				int j=scan.nextInt();
				int v=scan.nextInt();
				int t=map[i][j];
				map[i][j]=v;
				mat[t].update0(i, j);
				mat[v].update1(i, j);
			}
		}
	}

	public static void main(String[] args) {
		new CountCross3467().run();
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值