1. 程式人生 > 實用技巧 >#floyd,分治#D 路徑之和

#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);
}