CodeForces 57 D.Journey(组合数学)

Description

给出一 n×m n × m 的地图, X X 表示该位置被占,否则是空,问任意两个空位置的最短距离的期望值,两个位置是可达的当且仅当两个位置均为空且共边,保证任意两个X不会同行同列也不会对角相邻

Input

第一行两个整数 n,m n , m 表示地图行列数,之后输入一 n×m n × m 的地图 (2n,m1000) ( 2 ≤ n , m ≤ 1000 )

Output

输出任意两个空位置最短距离的期望值,误差不超过 106 10 − 6

Sample Input

2 2
.. . .
.X . X

Sample Output

0.888888888889

Solution

首先不考虑 X X 对最短路的影响,那么两个位置(x1,y1) (x2,y2) ( x 2 , y 2 ) 的最短距离即为 |x2x1|+|y2y1| | x 2 − x 1 | + | y 2 − y 1 | ,对于位置 (x,y) ( x , y ) ,其到这 nm n m 个点的最短距离之和为:

i=1nj=1m(|xi|+|yj|)=mi=1n|xi|+nj=1m|yj|=m(C2x+C2nx+1)+n(C2y+C2my+1) ∑ i = 1 n ∑ j = 1 m ( | x − i | + | y − j | ) = m ∑ i = 1 n | x − i | + n ∑ j = 1 m | y − j | = m ⋅ ( C x 2 + C n − x + 1 2 ) + n ⋅ ( C y 2 + C m − y + 1 2 )

根据该公式可以求出任意两点最短距离和 S1 S 1 ,所有 X X 到其他点的最短距离和S2,任意两个 X X 的最短距离和S3

注意其中 S2,S3 S 2 , S 3 在考虑 X X 对最短路的影响时也不变,因为任意两个X不同行不同列不对角相邻,这样就不会出现连续的(此处连续指的是把共边或对角相邻的点视为相邻)的 X X 把这个X和其他点完全隔开,如此求可以求出不考虑 X X 对最短路影响时任意两个空位置的最短距离和S12(S2S3)S3=S12S2+S3

但是注意到 X X 会对两个空位置之间的最短距离造成影响,对于(x,y)处的 X X ,第x行和第 y y 列只有这一个X,那么第 x x 行前y1个空位置到后 my m − y 个位置的最短路被 X X 隔离开了,必须绕路才可达,绕路距离至少多2,且多 2 2 方案存在,因为X一圈都没有其他 X X ,所以只需多走两步绕过X即可,这样对每个点对都会多 2 2 步距离,总共就会多4(y1)(my)步距离,同理考虑第 y y 列会多出4(x1)(nx)步距离

上面只考虑了 X X 对其所在行和所在列的影响,但是注意到如果一些相邻行都有X且这些 X X 的列数单调,或者一些相邻列都有X且这些 X X 的行数单调,例如第i行到第 j j 行都有X,且这些 X X 的列数为ai<ai+1<...<aj,那么第 i i 行前ai1个元素到第 k k 行后nak个元素的所有最短路径均会被这些 X X 阻挡,必须要绕过一个X才可达,故最短距离步数增加 2 2 ,总的多出4(ai1)(mak)步, i<kj i < k ≤ j ,以此累计多出的步数加到答案里即可

现在求出了所有空位置最短距离之和,除以 (nmnum)2 ( n m − n u m ) 2 即为答案,其中 num n u m X X <script type="math/tex" id="MathJax-Element-69">X</script>的个数

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=1005;
int n,m,x[maxn],y[maxn],row[maxn],col[maxn];
char s[maxn][maxn]; 
int C(int n)
{
    return n*(n-1)/2;
}
ll Solve(int x,int y)
{
    return (ll)m*(C(x)+C(n-x+1))+(ll)n*(C(y)+C(m-y+1));
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        int res=0;
        memset(row,0,sizeof(row));
        memset(col,0,sizeof(col));
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s[i]+1);
            for(int j=1;j<=m;j++)
                if(s[i][j]=='X')
                    row[i]=j,col[j]=i,x[res]=i,y[res++]=j;
        }
        ll sum0=0,sum1=0,sum2=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                sum0+=Solve(i,j);
        for(int i=0;i<res;i++)
            for(int j=0;j<res;j++)
                sum1+=abs(x[j]-x[i])+abs(y[j]-y[i]);
        for(int i=0;i<res;i++)sum2+=Solve(x[i],y[i]);
        sum2=2ll*sum2-sum1;
        sum0-=sum2;
        for(int i=1;i<=n;i++)
            if(row[i])
            {
                sum0+=4ll*(row[i]-1)*(m-row[i]);
                for(int j=i-1;j>=1;j--)
                    if(!row[j]||row[j]<row[j+1])break;
                    else sum0+=4ll*(row[i]-1)*(m-row[j]);
                for(int j=i+1;j<=n;j++)
                    if(!row[j]||row[j]<row[j-1])break;
                    else sum0+=4ll*(row[i]-1)*(m-row[j]);
            }
        for(int i=1;i<=m;i++)
            if(col[i])
            {
                sum0+=4ll*(col[i]-1)*(n-col[i]);
                for(int j=i-1;j>=1;j--)
                    if(!col[j]||col[j]<col[j+1])break;
                    else sum0+=4ll*(col[i]-1)*(n-col[j]);
                for(int j=i+1;j<=m;j++)
                    if(!col[j]||col[j]<col[j-1])break;
                    else sum0+=4ll*(col[i]-1)*(n-col[j]);
            }       
        double temp=1.0*(n*m-res)*(n*m-res);
        printf("%.10f\n",1.0*sum0/temp);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值