JZOJ 3053 【NOIP2012模拟10.25】旅行

旅行

题目大意

给定一个 n m列的字符矩阵, 告诉你哪些格子可以进入,哪些不可以。移动的规则为:每秒钟以上下左右四个方向之一移动一格,不能进入障碍。
计算:在空地中随机选择起点和终点(可以重合,此时最短耗时为 0 ),从起点移动到终点最短耗时的平均值。
每一行每一列至多有1个障碍,并且障碍不在对角线方向相邻

数据范围

2 <=n, m <=1000

题解

首先,这题的解法比较玄学。
首先,一对点对对应着一条路径。
暂且假设所有路径的的时间总和为所有点对的曼哈顿距离。
先求出曼哈顿距离和,再加上少了的那一部分。
那少了的那一部分怎么算呢?
我们看到障碍这里写图片描述这个神奇的条件。
这意味着什么呢?
这意味着一个点对的路径长度要么为它们的曼哈顿距离,要么为曼哈顿距离+2。(这很显然,请读者自行思考)
这下问题就简单多了,我们只需路径长度为曼哈顿距离+2的点对数就可以知道答案了。
可是我们应该怎么求满足条件的点对数呢?
看到下图,由红色的点走到蓝色的点的路径长度为曼哈顿距离+2(需要绕行,X表示障碍),我们找一下这些点对有什么共性。

这里写图片描述

从直观上来看,一个 X 下方的点到这个X上方的点需要绕行。如果这个 X 右边一列(或者左边一列)包含X 且 在本列 X 之上方,那么到这个X上方的点也需要绕行。横过来考虑是类似的。
根据以上规律,统计出需要+ 2 <script type="math/tex" id="MathJax-Element-29">2</script>的点对数加入答案即可。

Code(Pascal)

var
    n,m,j,k,l,i,p:longint;
    ans,kk,o:int64;
    ch:array[0..2000,0..2000] of char;
    hq,lq,hh,ll:array[-1..2000] of int64;
begin
    readln(n,m);
    for i:=1 to n do
    begin
        for j:=1 to m do
        read(ch[i,j]);
        readln;
    end;
    for i:=1 to n do
    begin
        for j:=1 to m do
        if ch[i,j]='.' then inc(hq[i]);
        hq[i]:=hq[i-1]+hq[i];
        kk:=kk+hq[i-1];
        ans:=ans+kk*(hq[i]-hq[i-1]);
    end;
    for i:=1 to n do
    hh[i]:=maxlongint;
    for i:=1 to m do
    ll[i]:=maxlongint;
    for i:=1 to n do
    for j:=1 to m do
    if ch[i,j]='X' then
    begin
        hh[i]:=j;
        ll[j]:=i;
    end;
    kk:=0;
    for j:=1 to m do
    begin
        for i:=1 to n do
        if ch[i,j]='.' then inc(lq[j]);
        lq[j]:=lq[j-1]+lq[j];
        kk:=kk+lq[j-1];
        ans:=ans+kk*(lq[j]-lq[j-1]);
    end;
    for j:=1 to m do
    if ll[j]<>maxlongint then
    begin
        p:=n-ll[j];
        o:=ll[j]-1;
        k:=j;
        while (k<m) and (ll[k+1]<ll[k]) do
        begin
            inc(k);
            o:=o+ll[k]-1;
        end;
        k:=j;
        while (k>1) and (ll[k-1]<ll[k]) do
        begin
            dec(k);
            o:=o+ll[k]-1;
        end;
        ans:=ans+p*o*2;
    end;
    for i:=1 to n do
    if hh[i]<>maxlongint then
    begin
        p:=m-hh[i];
        o:=hh[i]-1;
        k:=i;
        while (k<n) and (hh[k+1]<hh[k]) do
        begin
            inc(k);
            o:=o+hh[k]-1;
        end;
        k:=i;
        while (k>1) and (hh[k-1]<hh[k]) do
        begin
            dec(k);
            o:=o+hh[k]-1;
        end;
        ans:=ans+o*p*2;
    end;
    o:=n*m;
    ans:=ans*2;
    for i:=1 to n do
    for l:=1 to m do
    if ch[i,l]='X' then dec(o);
    o:=o*o;
    writeln((ans/o):0:4);
end.
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值