题意:一块N*N大的蛋糕M次讯问,每次切走一个矩形,问每次切的矩形被分成了多少块(1 <= N <= 1000, 1 <= M <= 10000)。
解法:显然是求某个区域内的连通分量数目,由于这个区域内的元素在访问后会被切除,因此每个元素只被访问一次,因此可以通过dfs或bfs求得某个区域内的连通分量的个数,均摊复杂度。
但是每次寻找遍历的起始点(即区域内未被切走的蛋糕)复杂度为O(N^2),因此需要优化,最坏情况是怎么产生的呢?当某个区域内有连续的已经切走的蛋糕,那我们在寻找未切走的蛋糕时需要逐一判断,产生复杂度,因此如果遇到连续的切走的区间,如果能够在短时间内跳过它,那么复杂度就可以得到控制。类似于supermarket的那个题目,查询未被占用的最左元素,我们用并查集维护。对于每一行维护一个并查集,f[x]代表x开始连续的被切走的蛋糕的最右边的那块,当某块蛋糕杯切走且它右边相邻的也被切走,就将他合并到右边的元素,这样每次查询寻找遍历起点的复杂度就变为O(N)。
注:dfs会爆栈,显然应该使用bfs
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class CutingCake2843 {
class Union {
int f[];
Union(int n) {
f= new int[n+10];
for (int i = 1; i <= n; i++)
f[i] = i;
}
int find(int x) {
if (x != f[x])
f[x] = find(f[x]);
return f[x];
}
}
class node{
int x,y;
node(int x,int y){
this.x=x;
this.y=y;
}
}
node stack[]=new node[1010*1010];
boolean vis[][]=new boolean[1010][1010];
int minx, miny, maxx, maxy, n,top;
boolean test(int x,int y){
if (x <minx|| x>maxx||y<miny||y>maxy)
return false;
if (vis[x][y])
return false;
stack[top++]=new node(x,y);
vis[x][y]=true;
return true;
}
void bfs(int x, int y) {
top=0;
stack[top++]=new node(x,y);
while(top!=0){
x=stack[--top].x;
y=stack[top].y;
vis[x][y] = true;
if(vis[x][y+1])
set[x].f[y]=y+1;
if(vis[x][y-1])
set[x].f[y-1]=y;
test(x,y+1);
test(x,y-1);
test(x-1,y);
test(x+1,y);
}
}
StreamTokenizer in = new StreamTokenizer(new BufferedReader(
new InputStreamReader(System.in)));
int nextInt() throws IOException {
in.nextToken();
return (int) in.nval;
}
Union set[] = new Union[1010];
void run() throws IOException {
n = nextInt();
int m = nextInt();
for (int i = 1; i <= n; i++)
set[i] = new Union(n);
while (m-- > 0) {
minx = nextInt();
miny = nextInt();
maxx = nextInt();
maxy = nextInt();
int res = 0;
for (int i = minx; i <= maxx; i++)
for (int j = miny; j <= maxy;j = set[i].find(j)+1)
if (!vis[i][j]) {
bfs(i, j);
res++;
}
System.out.println(res);
}
}
public static void main(String[] args) throws IOException {
new CutingCake2843().run();
}
}