2373 最短路和
小D生活的城市可以抽象为一个H×W的网格图,左上角为位置(0,0),右下角为位置(H-1,W-1)。
每个位置可以上下左右走,但是不能越出边界,也就是每次走到的位置(X,Y)都要满足0≤X<H,0≤Y<W。
现在这个网格图上有K个障碍点是不能通行的。现在小D想知道,对于网格图上任意两个不是障碍的格子,
他们的最短路是多少。为了输出方便,你只需要输出所有答案的和即可。具体来说,你需要算出,
网格图上,任意一对均不是障碍的位置,他们的最短路的和。
一个位置(A,B)到另外一个位置(X,Y)的最短路的定义是:从(A,B)出发,走到(X,Y),中途不经过任何障碍点,
且不越过边界的所有路径中,最少经过的边数。注意一对位置只需要算一遍。
输入
第一行包含三个整数H,W,K。
接下来K行,每行给定一个坐标(X,Y),代表一个障碍点的位置。
对于20%的数据,H×W≤5000
对于40%的数据,H,W≤10000
对于60%的数据,H,W≤10^6
对于100%的数据,H,W≤10^9,N≤50,0≤x_i<H,0≤y_i<W,保证给定的障碍点不重复,所有非障碍点都连通,至少存在一个非障碍点。
输出
输出一个整数,表示答案。答案请对10^9+7取模。
输入样例
4 4 3
0 2
2 1
3 2
输出样例
258
分析:
可以注意到题目的障碍数非常少,联想到要把大块的空白区域缩成一块。首先考虑缩行。
对于连续的两行i,i+1,假如i,i+1这两行上,一个障碍都没有,那么对于所有在i行上方的起点s,
到所有i+1行下方的终点t,都会经过这两行恰好1次。由于这两行是完全一样的,
我们可以把他们压缩成一行。同时把每个位置标记为(对应原来的2个位置),在这个子问题的答案下,
加上所有上方到下方的点,这两行对答案的贡献(可以直接算出来),就是原问题的答案了。
进一步的,可以发现,对于连续的一段[l,r],假如行l到行r,都是没有障碍的。
我们都可以把他们压缩成一行。我们可以模拟l和l+1合并,再到l+2,…一直到r。现在要做到快速把[l,r]合并,
就需要把过程中每两行对答案的贡献的公式加起来化简(自行推导,最后是一个平方和的形式)。
把行合并完后再对列做同样的事情。最后最多只会剩下2N行与2N列。 剩下的问题就比较简单了。
现在的每个格子不再是一个格子,而是代表原来的多个。这个在求答案时乘一下就好。
当前的网格图大小是100*100级别。可以枚举每个格子,做一遍宽搜,求出到其他所有点的最短距离。
假如起点对应数量为c_s,终点对应数量为c_t,距离为d,他们对答案的贡献就是c_s×c_t×d。
总的复杂度就是O(N^4)。关键思想就是离散化。
放代码:
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9+7;
const int N = 110;
int fpow(int x,int p){
int rs=1;
while(p){
if(p&1) rs=1LL*rs*x%MOD;
x=1LL*x*x%MOD; p>>=1;
}
return rs;
}
struct Node{
int x,y;
Node(int _x=0,int _y=0){
x=_x; y=_y;
}
};
vector<Node> ob;
int x[N],y[N],h,w,k,n,m,inv2,inv6;
int cx[N],cy[N],c[N][N],cn[N][N];
int d[N][N],dx[4]={0,-1,1,0},dy[4]={-1,0,0,1};
bool check(Node p){
return p.x>=0&&p.x<n&&p.y>=0&&p.y<m&&d[p.x][p.y]==-1&&!cn[p.x][p.y];
}
int merge(int a,int b,int d,int n){
int rs=1LL*a*b%MOD*n%MOD;
b=(b-a+MOD)%MOD;
rs=(rs+1LL*b*n%MOD*(n+1)%MOD*inv2%MOD*d%MOD)%MOD;
rs=(rs-1LL*n*(n+1)%MOD*(2*n+1)%MOD*inv6%MOD*d%MOD*d%MOD+MOD)%MOD;
return rs;
}
Node Q[N*N]; int front,tail;
void bfs(Node cur){
Node nex(0,0); front=0,tail=0;
for(int i=0;i<n;++i)
for(int j=0;j<m;++j) d[i][j]=-1;
d[cur.x][cur.y]=0;
Q[tail++]=cur;
while(front<tail){
cur=Q[front]; front++;
for(int i=0;i<4;++i){
nex.x=cur.x+dx[i];
nex.y=cur.y+dy[i];
if(check(nex)){
d[nex.x][nex.y]=d[cur.x][cur.y]+1;
Q[tail++]=nex;
}
}
}
}
int ans,tp,tot;
int main(){
inv2=fpow(2,MOD-2); inv6=fpow(6,MOD-2);
scanf("%d %d %d",&h,&w,&k);
for(int i=0;i<k;++i){
scanf("%d %d",&x[i],&y[i]);
x[i+k]=x[i]+1; y[i+k]=y[i]+1;
ob.push_back(Node(x[i],y[i]));
}
n=m=2*k; tot=(1LL*h*w-k+MOD)%MOD;
x[n++]=0; x[n++]=h; y[m++]=0; y[m++]=w;
sort(x,x+n); n=unique(x,x+n)-x-1;
sort(y,y+m); m=unique(y,y+m)-y-1;
for(auto p:ob){
p.x=lower_bound(x,x+n,p.x)-x;
p.y=lower_bound(y,y+m,p.y)-y;
cx[p.x]++; cy[p.y]++; cn[p.x][p.y]++;
}
for(int i=n-1,c=0;i>=0;--i){
c=(c+1LL*(x[i+1]-x[i])*w%MOD-cx[i]+MOD)%MOD;
ans=(ans+merge((tot-c+MOD)%MOD,c,w,x[i+1]-x[i]-1))%MOD;
}
for(int i=m-1,c=0;i>=0;--i){
c=(c+1LL*(y[i+1]-y[i])*h%MOD-cy[i]+MOD)%MOD;
ans=(ans+merge((tot-c+MOD)%MOD,c,h,y[i+1]-y[i]-1))%MOD;
}
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
c[i][j]=1LL*(x[i+1]-x[i])*(y[j+1]-y[j])%MOD;
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
if(cn[i][j]) continue;
bfs(Node(i,j));
for(int p=0;p<n;++p){
for(int q=0;q<m;++q){
if(cn[p][q]) continue;
tp=(tp+1LL*c[i][j]*c[p][q]%MOD*d[p][q])%MOD;
}
}
}
}
tp=1LL*tp*inv2%MOD; ans=(ans+tp)%MOD;
printf("%d\n",ans);
}