jzoj P2249 蒸发学水

83 篇文章 0 订阅
30 篇文章 0 订阅

题目大意:

机房的位置一共有n 行m 列,一开始每个位置都有一滴学水,TerryHu 决定在每一个时刻选择
一滴学水进行蒸发,直到机房里不再存在学水。
TerryHu 想知道在每个时刻之后,机房里剩下的学水构成了几个联通块。

对于60% 的数据:n,m<= 50;
对于100% 的数据:n,m<= 1000。

题解:

邻接表+并查集:
这题我们直接暴力的去做,很明显只能过60分的点
即暴力的枚举,时间复杂度:O((NM)^2)

我们用邻接表去记录不同大小的数的位置
然后我们逆向思维地用并查集去做
我们从后往前即学水大到小依次加入,这样我们就可以保证每次得到的ans[x]就是第x时刻的联通块数
对于每次加入的[x,y]
如果周边没有学水,那么ans+1
如果周边有学水,而且有y滴,那么很明显会想到ans-y+1,不过有y滴并不代表一定有y个联通块,因为y滴学水之间可能有相通的,这时候用并查集去处理即可。

代码:

const
    dx:array [1..4] of longint=(1,-1,0,0);
    dy:array [1..4] of longint=(0,0,-1,1);
var
    a:array [0..1001,0..1001] of longint;
    f:array [0..1000001] of longint;
    list:array [0..1000001,1..2] of longint;
    fd,next:array [0..1001,0..1001,1..2] of longint;
    mind:array [1..4] of boolean;
    xx,yy,ans,x,y,i,j,k,n,m:longint;

procedure find(cx,cy:longint);
begin
    if (fd[cx,cy,1]=cx) and (fd[cx,cy,2]=cy) then
    begin
         xx:=cx;
         yy:=cy;
         exit;
    end;
    find(fd[cx,cy,1],fd[cx,cy,2]);
    fd[cx,cy,1]:=xx;
    fd[cx,cy,2]:=yy;
end;

procedure insert(cx,cy:longint);
var
    i,j,k,d,x1,y1,x2,y2:longint;
begin
    k:=0;
    d:=0;
    fillchar(mind,sizeof(mind),false);
    for i:=1 to 4 do
         if a[cx+dx[i],cy+dy[i]]=1 then
         begin
             inc(k);
             if not(mind[i]) then
             begin
                find(cx+dx[i],cy+dy[i]);
                x1:=xx; y1:=yy;
                for j:=1 to 4 do
                  if (i<>j) and (not(mind[j])) and (a[cx+dx[j],cy+dy[j]]=1) then
                    begin
                       find(cx+dx[j],cy+dy[j]);
                       x2:=xx; y2:=yy;
                       if (x1=x2) and (y1=y2) then
                        begin
                           mind[i]:=true;
                           mind[j]:=true;
                        end;
                    end;
                inc(d);
             end;
         end;

    for i:=1 to 4 do
      if a[cx+dx[i],cy+dy[i]]=1 then
        begin
             find(cx+dx[i],cy+dy[i]);
             fd[xx,yy,1]:=cx;
             fd[xx,yy,2]:=cy;
        end;
    if d=0 then
    begin
         ans:=ans-k+1;
         exit;
    end;
    ans:=ans-d+1;
end;

begin
    assign(input,'evaporate.in'); reset(input);
      assign(output,'evaporate.out'); rewrite(output);
      readln(n,m);
      for i:=1 to n do
        begin
          for j:=1 to m do
               begin
                   read(k);
                   next[i,j,1]:=list[k,1];
                   next[i,j,2]:=list[k,2];
                   list[k,1]:=i;
                   list[k,2]:=j;
               end;
             readln;
        end;
      ans:=0;
     for k:=n*m downto 1 do
        begin
          f[k]:=ans;
          x:=list[k,1]; y:=list[k,2];
          while x>0 do
          begin
              a[x,y]:=1;
              fd[x,y,1]:=x;
              fd[x,y,2]:=y;
              i:=x; j:=y;
              insert(x,y);
              x:=next[i,j,1];
              y:=next[i,j,2];
          end;

      end;
      for i:=1 to n*m do writeln(f[i]);
     close(input); close(output);
end.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值