题意:给出一个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(); } }