正题
题目链接:http://noip.ybtoj.com.cn/contest/108/problem/4
题目大意
nnn个点,每次只封闭一个点,求剩下点对两两之间的最短路和。
解题思路
FlodyFlodyFlody中如果枚举kkk时不管某一个点,那么就可以求不经过那个点的路径。考虑分治求解
我们每次处理到[l,r][l,r][l,r]时我们先计算出[mid+1,r][mid+1,r][mid+1,r]的贡献(也就是枚举这里的kkk),然后递归到[l,mid][l,mid][l,mid]的区间。处理完后清除[mid+1,r][mid+1,r][mid+1,r]的贡献在处理[l,mid][l,mid][l,mid]的贡献递归到[mid+1,r][mid+1,r][mid+1,r]区间。
这样当l=rl=rl=r时只有lll这个位置没有处理贡献,就可以统计答案了。
时间复杂度O(n3logn)O(n^3\log n)O(n3logn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=321;
ll n,f[N][N],ans,g[30][N][N];
void solve(ll dep,ll l,ll r){if(l==r){for(ll i=1;i<=n;i++)for(ll j=1;j<=n;j++){if(i==l||j==l)continue;if(f[i][j]<1e18)ans+=f[i][j];else ans--;}return;}ll mid=(l+r)>>1;memcpy(g[dep],f,sizeof(g[dep]));for(ll k=mid+1;k<=r;k++)for(ll i=1;i<=n;i++)for(ll j=1;j<=n;j++)f[i][j]=min(f[i][j],f[i][k]+f[k][j]);solve(dep+1,l,mid); memcpy(f,g[dep],sizeof(f));for(ll k=l;k<=mid;k++)for(ll i=1;i<=n;i++)for(ll j=1;j<=n;j++)f[i][j]=min(f[i][j],f[i][k]+f[k][j]);solve(dep+1,mid+1,r);return;
}
int main()
{freopen("sum.in","r",stdin);freopen("sum.out","w",stdout);scanf("%lld",&n);for(ll i=1;i<=n;i++){for(ll j=1;j<=n;j++){scanf("%lld",&f[i][j]);if(f[i][j]==-1)f[i][j]=1e18;}}solve(0,1,n);printf("%lld",ans);
}