如果一条一条路算的话非常不好去重,不妨一条一条边算
30pts:先跑一遍Floyd预处理,枚举点对和每条边,O(1)判断边是否在点对的最短路上,总时间复杂度O(n^2m)
100pts:考虑优化上述解法,每次就不用枚举n^2条边:预处理以k为端点,且位于(i,k)最短路径上的边的价值和cnt[i][k],这个预处理需要枚举i,k并检查以k为以端点的边的另一端点是否在(i,k)的最短路上,复杂度O(n^3)
这样ans[i][j]=\sum\limits_kcnt[i][k] (k位于(i,j)最短路上),这样对于每个点对只需要枚举O(n)个点,总时间复杂度O(n^3)
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
using namespace std;
const int MAX=5e2+5;
const int inf=1061109567;
int n,m;
int len[MAX][MAX],value[MAX][MAX];
int dis[MAX][MAX],cnt[MAX][MAX];
inline void pre(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)if(dis[i][k]!=-1 && dis[k][j]!=-1){
if(dis[i][j]==-1 || dis[i][j]>dis[i][k]+dis[k][j])dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
for(int i=1;i<=n;i++){
for(int k=1;k<=n;k++)if(dis[i][k]!=-1){
for(int j=1;j<=n;j++)if(j!=k && dis[i][j]!=-1 && len[j][k]!=-1 && dis[i][j]+len[j][k]==dis[i][k])cnt[i][k]+=value[j][k];
}
}
}
signed main(){
scanf("%lld %lld",&n,&m);
memset(dis,-1,sizeof(dis));
for(int x,y,i=1;i<=m;i++)scanf("%lld %lld",&x,&y),scanf("%lld %lld",&dis[x][y],&value[x][y]),dis[y][x]=dis[x][y],value[y][x]=value[x][y];
for(int i=1;i<=n;i++)dis[i][i]=0;
memcpy(len,dis,sizeof(dis));
pre();
for(int i=1;i<=n;i++){
for(int ans,j=i+1;j<=n;j++){
ans=0;
for(int k=1;k<=n;k++)if(dis[i][k]+dis[k][j]==dis[i][j])ans+=cnt[i][k];
printf("%lld ",ans);
}
}
return 0;
}