#floyd,分治#D 路径之和

题目

对于每个\(y\),求除了\(y\)之外,其余的所有点组成的有序点对\((x,z)\)
不经过\(y\)的最短路长度之和(不存在即为-1)。\(n\leq 320\)


分析

太妙了,首先用floyd朴素就是\(O(n^4)\)
由于朴素算法有很多冗余状态,
考虑分治处理,\([l,r]\)表示当前区间以外进行过floyd
对于\(l=r\)的时候直接统计答案,否则拆成\([l,mid],[mid+1,r]\)
然后计算左区间对右区间和右区间对左区间的贡献,时间复杂度\(O(n^3\log n)\)


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
using namespace std;
const int N=321,inf=0x3f3f3f3f;
int dis[10][N][N],n; long long ans;
inline signed iut(){
    rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
inline void Min(int &a,int b){a=a<b?a:b;}
inline void divi(int dep,int l,int r){
	if (l==r){
		for (rr int i=1;i<=n;++i)
		for (rr int j=1;j<=n;++j)
		if ((i^j)&&(i^r)&&(j^r)){
			if (dis[dep][i][j]^inf) ans+=dis[dep][i][j];
			    else --ans;
		}
		return;
	}
	rr int mid=(l+r)>>1;
	memcpy(dis[dep+1],dis[dep],sizeof(dis[dep]));
	for (rr int k=mid+1;k<=r;++k)
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<=n;++j)
	if ((i^j)&&(i^k)&&(j^k))
	    Min(dis[dep+1][i][j],dis[dep+1][i][k]+dis[dep+1][k][j]);
	divi(dep+1,l,mid);
	memcpy(dis[dep+1],dis[dep],sizeof(dis[dep]));
	for (rr int k=l;k<=mid;++k)
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<=n;++j)
	if ((i^j)&&(i^k)&&(j^k))
	    Min(dis[dep+1][i][j],dis[dep+1][i][k]+dis[dep+1][k][j]);
	divi(dep+1,mid+1,r);
}
signed main(){
	freopen("sum.in","r",stdin);
	freopen("sum.out","w",stdout); 
	n=iut();
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<=n;++j){
		dis[0][i][j]=iut();
		if (dis[0][i][j]<0)
		    dis[0][i][j]=inf;
	}
	divi(0,1,n);
	return !printf("%lld",ans);
}
posted @ 2020-11-02 16:12  lemondinosaur  阅读(99)  评论(0编辑  收藏  举报