题目传送门
题目大意
输入一个n*m的矩阵,每个格子里都有一个[1,
109
10
9
]的正整数。再输入T个整数
ti
t
i
,对于每个
ti
t
i
,输出矩阵中大于ti的数组成了多少个联通块(上下左右相连,斜着不联通)。
有Z组数据
数据范围
1≤n,m≤1000
0≤t1≤t2≤···≤tT≤
109
10
9
1≤T≤
105
10
5
0≤Z≤20
【题解】
首先想到暴力方法,对于每个
ti
t
i
,进行遍历,时间复杂度O(ZTnm),完全Tle
然后我发现可以利用0≤
t1
t
1
≤
t2
t
2
≤···≤
tT
t
T
≤
109
10
9
这一性质,用离线的方法。
而且我发现,
ti
t
i
越小,联通块的总面积是越大的,而且是从
ti+1
t
i
+
1
的联通块发展过来的,那么就倒序扫t数组,同时扩散联通块
然后我发现扫t的过程中很难确定到底矩阵中那个数大于
ti
t
i
,由于
ti
t
i
范围可以到
109
10
9
,不能开桶,所以想到对矩阵中的数进行排序
排序可以把矩阵拉成一个序列进行排序,排序之前记录好每个数的坐标即可
然后从大到小扫矩阵中的数,同时跟一个j,表示当前的数x,是满足
tj
t
j
< x<
tj+1
t
j
+
1
的,同时再跟一个ans,初始化为0,然后每进一个数,ans+1,用并查集进行合并,若四周的数大于
tj
t
j
,而且两数属于不同的块,那么合并、ans-1
分析时间复杂度:O(Z(nmlog(nm)+nmα(nm)+T))其中α为并查集常数,很小
Code:
var
a:array[0..2000000] of record
x,y,num:longint;
end;
s:array[0..1000,0..1000] of record
num,x:longint;
end;
b,f,print:array[0..2000000] of longint;
p,q,n,m,ans,x,y,z,i,j,k,t,sum1,sum2:longint;
dx:array[1..4] of longint = (-1,0,0,1);
dy:array[1..4] of longint = (0,-1,1,0);
function get(k:longint):longint;
begin
if f[k] <> k then f[k] := get(f[k]);
get := f[k];
end;
procedure swap(var x,y:longint);
var
tmp:longint;
begin
tmp := x; x := y; y := tmp;
end;
procedure sort(l,r:longint);
var
i,j,mid:longint;
begin
i := l; j := r; mid := a[(l + r) >> 1].num;
repeat
while a[i].num > mid do inc(i);
while a[j].num < mid do dec(j);
if i <= j then
begin
swap(a[i].x,a[j].x);
swap(a[i].y,a[j].y);
swap(a[i].num,a[j].num);
inc(i); dec(j);
end;
until i > j;
if i < r then sort(i,r);
if l < j then sort(l,j);
end;
begin
readln(q);
for p := 1 to q do
begin
readln(n,m);
for i := 1 to n do
begin
for j := 1 to m do
begin
x := (i - 1) * m + j;
read(a[x].num);
a[x].x := i; a[x].y := j;
end;
readln;
end;
sort(1,n * m);
for i := 1 to n * m do
begin
s[a[i].x][a[i].y].num := a[i].num;
s[a[i].x][a[i].y].x := i;
end;
readln(t);
for i := 1 to t do read(b[i]);
for i := 1 to n * m do f[i] := i;
ans := 0; j := t; a[n * m + 1].num := 0;
for i := 1 to n * m + 1 do
begin
while a[i].num <= b[j] do
begin
print[j] := ans;
dec(j);
if j = 0 then break;
end;
if j = 0 then break;
inc(ans);
for k := 1 to 4 do
begin
x := a[i].x + dx[k];
y := a[i].y + dy[k];
if (x < 1) or (y < 1) or (x > n) or (y > m) then continue;
if s[x][y].num > b[j] then
begin
sum1 := get(s[x][y].x); sum2 := get(i);
if sum1 <> sum2 then
begin
f[sum1] := sum2;
dec(ans);
end;
end;
end;
end;
for i := 1 to t do write(print[i],' ');
writeln;
end;
end.