给定一个只含0和1二维数组matrix,第0行表示天花板。每个位置认为与上、下、左、右四个方向有粘性,比如:matrix =  
1 0 0 1 0
1 0 0 1 1
1 1 0 1 1
1 0 0 0 0
0 0 1 1 0
注意到0行0列是1,然后能延伸出5个1的一片。同理0行3列也是1,也能延伸出5个1的一片。
注意到4行2列是1,然后能延伸出2个1的一片。其中有两片1是粘在天花板上的,而4行2列延伸出来的这片,认为粘不住就掉下来了。
在给定一个二维数组bomb,表示炸弹的位置,比如:
bomb = 2 0
1 3
1 4
0 3
第一枚炮弹在2行0列,该处的1直接被打碎,然后会有2个1掉下来。
第二枚炮弹在1行3列,该处的1直接被打碎,不会有1掉下来,因为这一片1还能粘在一起。
第三枚炮弹在1行4列,该处的1直接被打碎,然后会有2个1掉下来。
第四枚炮弹在0行3列,该处的1直接被打碎,不会有1掉下来,因为这一片1只剩这一个了。
根据matrix和bomb,返回结果[2,0,2,0]。
import java.util.*;

public class Main {
private static void changeMatrix(Point[][] matrix, int[][] hits) {
if (matrix == null || matrix.length == 0 || hits == null || hits.length == 0) {
return;
}
for (int i = 0; i < hits.length; ++i) {
if (matrix[hits[i][0]][hits[i][1]].value == 1) {
matrix[hits[i][0]][hits[i][1]].value = 2;
}
}
}

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
int n = in.nextInt();
int m = in.nextInt();
Point[][] points = new Point[n][m];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
points[i][j] = new Point(in.nextInt());
}
}
int k = in.nextInt();
int[][] hits = new int[k][2];
for (int i = 0; i < k; ++i) {
hits[i][0] = in.nextInt();
hits[i][1] = in.nextInt();
}
changeMatrix(points, hits);
UnionSet unionSet = new UnionSet(points);
unionSet.init();
int[] ret = new int[k];
Arrays.fill(ret, -1);
for (int i = 0; i < k; ++i) {
if (points[hits[i][0]][hits[i][1]].value == 2) {
}
}
for (int i = k - 1; i >= 0; --i) {
if (ret[i] == -1 && points[hits[i][0]][hits[i][1]].value == 2) {
ret[i] = unionSet.finger(hits[i][0], hits[i][1]);
}
}
for (int i = 0; i < k; ++i) {
System.out.println(ret[i]);
}
}
}
}

class Point {
int value;

public Point(int value) {
this.value = value;
}
}

class UnionSet {
private int total;
private int totalOnTheWall;
private int N;
private int M;
private Point[][] points;
private Map<Point, Point> fatherMap;
private Map<Point, Integer> sizeMap;
private Set<Point> onWallSet;

public UnionSet(Point[][] points) {
this.totalOnTheWall = 0;
this.points = points;
this.N = points.length;
this.M = points[0].length;
this.fatherMap = new HashMap<>();
this.sizeMap = new HashMap<>();
this.onWallSet = new HashSet<>();
}

private boolean isValid(int x, int y) {
if (x < 0 || y < 0 || x >= N || y >= M) {
return false;
}
return true;
}

private boolean isOne(int x, int y) {
if (!isValid(x, y)) {
return false;
}
return points[x][y].value == 1;
}

public int finger(int row, int col) {
Point cur = points[row][col];
cur.value = 1;
if (row == 0) {
onWallSet.add(cur);
totalOnTheWall++;
}
fatherMap.put(cur, cur);
sizeMap.put(cur, 1);
int pre = totalOnTheWall;
connectAround(row, col);
Point father = find(cur);
if (onWallSet.contains(father)) {
int now = totalOnTheWall;
return now - pre - 1;
} else {
return sizeMap.get(father) - 1;
}
}

private Point find(Point x) {
Point f = fatherMap.get(x);
if (f == x) {
return x;
}
f = findPlus(f);
fatherMap.put(x, f);
return f;
}

private Point findPlus(Point x) {
Point root = fatherMap.get(x);
while (root != fatherMap.get(root)) {
root = fatherMap.get(root);
}
while (x != root) {
Point father = fatherMap.get(x);
fatherMap.put(x, root);
x = father;
}
return root;
}

private void connectAround(int x, int y) {
int[] dx = new int[]{0, 1, 0, -1};
int[] dy = new int[]{1, 0, -1, 0};
for (int i = 0; i < 4; ++i) {
int nx = x + dx[i];
int ny = y + dy[i];
connect(x, y, nx, ny);
}
}

private void connect(int x1, int y1, int x2, int y2) {
if (!isOne(x1, y1) || !isOne(x2, y2)) {
return;
}
union(x1, y1, x2, y2);
}

public void init() {
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
if (points[i][j].value == 1) {
fatherMap.put(points[i][j], points[i][j]);
sizeMap.put(points[i][j], 1);
if (i == 0) {
onWallSet.add(points[i][j]);
totalOnTheWall++;
}
}
}
}
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
if (points[i][j].value == 1) {
connectAround(i, j);
}
}
}
}

private void union(int x1, int y1, int x2, int y2) {
Point p1 = points[x1][y1];
Point p2 = points[x2][y2];
Point f1 = findPlus(p1);
Point f2 = findPlus(p2);
if (f1 == f2) {
return;
}
int size1 = sizeMap.get(f1);
int size2 = sizeMap.get(f2);
boolean on1 = onWallSet.contains(f1);
boolean on2 = onWallSet.contains(f2);
if (size1 >= size2) {
fatherMap.put(f2, f1);
sizeMap.put(f1, size1 + size2);
if (on1 ^ on2) {
totalOnTheWall += on1 ? size2 : size1;
onWallSet.add(f1);
}
} else {
fatherMap.put(f1, f2);
sizeMap.put(f2, size1 + size2);
if (on1 ^ on2) {
totalOnTheWall += on1 ? size2 : size1;
onWallSet.add(f2);
}
}
}
}


心之所向,素履以往 生如逆旅,一苇以航