題意:一塊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();
}
}