【题解】UVa1665:Islands

版权声明:Fashion Education https://blog.csdn.net/ModestCoder_/article/details/81945452

题目传送门
题目大意
输入一个n*m的矩阵,每个格子里都有一个[1, 10 9 ]的正整数。再输入T个整数 t i ,对于每个 t i ,输出矩阵中大于ti的数组成了多少个联通块(上下左右相连,斜着不联通)。
有Z组数据
数据范围
1≤n,m≤1000
0≤t1≤t2≤···≤tT≤ 10 9
1≤T≤ 10 5
0≤Z≤20

【题解】
首先想到暴力方法,对于每个 t i ,进行遍历,时间复杂度O(ZTnm),完全Tle

然后我发现可以利用0≤ t 1 t 2 ≤···≤ t T 10 9 这一性质,用离线的方法。
而且我发现, t i 越小,联通块的总面积是越大的,而且是从 t i + 1 的联通块发展过来的,那么就倒序扫t数组,同时扩散联通块

然后我发现扫t的过程中很难确定到底矩阵中那个数大于 t i ,由于 t i 范围可以到 10 9 ,不能开桶,所以想到对矩阵中的数进行排序
排序可以把矩阵拉成一个序列进行排序,排序之前记录好每个数的坐标即可
然后从大到小扫矩阵中的数,同时跟一个j,表示当前的数x,是满足 t j < x< t j + 1 的,同时再跟一个ans,初始化为0,然后每进一个数,ans+1,用并查集进行合并,若四周的数大于 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.

猜你喜欢

转载自blog.csdn.net/ModestCoder_/article/details/81945452