传送门
题目大意:给你一个n个点、m条边的有向图,问你每条边被多少条不同的最短路经过,答案对10^9+7取模,其中n<=1500,m<=5000。
由于n、m都不大,一个显然的做法是枚举一个起点S,然后跑SPFA。对于一条边,如果它不在最短路图上,就肯定不会被算上。而一条边i->j在图上的充要条件是Dis[i]+l<=Dis[j],构出来这个图你就会发现这是个DAG。
然后DAG上所有边都是从S出发到某个点的最短路上的边。换而言之,从S出发沿一条路径访问到的每一个点,该点的最短路就是该路径的长度。所以很容易想到对于一条边u->v的答案贡献就是f[u]*g[v]。
其中f[u]代表沿S到u的路径方案数,g[v]代表v能到达的所有点的路径方案数(注意不是点的个数)。这里自行体会一下就能明白。
现在我们只需对DAG进行拓扑排序,然后正向递推出f,对于g,反着建一遍最短路图,然后同样也能算出来。
最后每条原边的答案就是∑f[u]*g[v]了。代码比较好写,但要建三个图,细节处理要注意(我一开始忘了第二次建的图是反过来的,导致答案算反了QAQ)。
#include
#define maxn 1555
#define maxm 5005
#define INF 0x7FFFFFFF
#define MOD 1000000007
using namespace std;
typedef long long LL;
int n, m;
int cur[2];
struct List{
int obj, len, id;
List *next;
}*head[2][maxn], Edg[2][maxm];
void Addedge(int p, int a, int b, int c, int id){
Edg[p][++cur[p]].next = head[p][a];
Edg[p][cur[p]].obj = b;
Edg[p][cur[p]].len = c;
Edg[p][cur[p]].id = id;
head[p][a] = Edg[p]+cur[p];
}
LL ans[maxm];
bool Vis[maxn];
int q[maxn];
int f[maxn], g[maxn], Dis[maxn], in[maxn];
void SPFA(int S){
int hh, tt;
q[hh = tt = 0] = S;
for(int i = 1; i <= n; i++) Dis[i] = INF, Vis[i] = false;
Dis[S] = 0;
Vis[S] = true;
while(hh <= tt){
int now = q[hh%n];
hh ++;
for(List *p = head[0][now]; p; p = p->next){
int v = p->obj, l = p->len;
if(Dis[now] + l < Dis[v]){
Dis[v] = Dis[now] + l;
if(!Vis[v]){
Vis[v] = true;
tt ++;
q[tt%n] = v;
if(Dis[q[hh%n]] > Dis[q[tt%n]]) swap(q[hh%n], q[tt%n]);
}
}
}
Vis[now] = false;
}
}
void Topo(int P, int *x){
cur[1] = -1;
for(int i = 1; i <= n; i++) head[1][i] = NULL, in[i] = 0, x[i] = P;
for(int i = 1; i <= n; i++){
if(Dis[i] == INF) continue;
for(List *p = head[0][i]; p; p = p->next){
int v = p->obj, l = p->len, id = p->id;
if(Dis[i] + l <= Dis[v]){
if(!P){
Addedge(1, i, v, l, id);
in[v] ++;
}
else{
Addedge(1, v, i, l, id);
in[i] ++;
}
}
}
}
int hh = 0, tt = -1;
for(int i = 1; i <= n; i++)
if(Dis[i] != INF && !in[i]) q[++tt] = i, x[i] = 1;
while(hh <= tt){
int now = q[hh++];
for(List *p = head[1][now]; p; p = p->next){
int v = p->obj;
x[v] += x[now];
in[v] --;
if(!in[v]) q[++tt] = v;
}
}
}
int main(){
scanf("%d%d", &n, &m);
int a, b, c;
cur[0] = -1;
for(int i = 1; i <= n; i++) head[0][i] = NULL;
for(int i = 1; i <= m; i++){
scanf("%d%d%d", &a, &b, &c);
Addedge(0, a, b, c, i);
}
for(int i = 1; i <= n; i++){
SPFA(i);
Topo(0, f);
Topo(1, g);
for(int j = 1; j <= n; j++)
for(List *p = head[1][j]; p; p = p->next){
int v = p->obj, id = p->id;
ans[id] = (ans[id] + 1LL * f[v] * g[j]) % MOD;
}
}
for(int i = 1; i <= m; i++)
printf("%lld\n", ans[i]);
return 0;
}