题目传送门

题目大意

给出一个 \(n\) 个点 \(m\) 条边的有向图,问每一条边在多少个最短路径中出现。

\(n\le 1500,m\le 5000\)

思路

算我孤陋寡闻了。。。

很显然,我们需要枚举一个起点 \(s\),然后跑一遍最短路,对于一条边 \((u,v,w)\),如果存在 \(\text{dist}(u)+w=\text{dist}(v)\),可以想到 \((u,v)\) 一定会产生答案,我们定义此类边叫做“最短路径图上的边”,它们构成的图叫做“最短路径图”。它有以下两个性质:

  • \(u\to v\) 的最短路径上的子路径 \(a\to b\) 也是最短路径

  • 最短路径图上不存在环

证明:

因为如果有环的话与最短路径图上的边的定义矛盾了,所以显然。

然后根据性质1我们就可以观察到我们可以在这个图上做 topo 排序,求出到 \(u\) 的最短路径数,\(v\) 到其它点的最短路径数,相乘即是答案。

时间复杂度 \(\Theta(VE)\),但是用 SPFA 的话还是可以跑很快的。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define mod 1000000007
#define MAXN 5005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} bool vis[MAXN],ins[MAXN];
int n,m,toop,st[MAXN],to[MAXN],wei[MAXN],nxt[MAXN],head[MAXN],dist[MAXN]; void Add_Edge (int u,int v,int w){
to[++ toop] = v,st[toop] = u,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;
} void SPFA (int s){
queue <int> q;
while (!q.empty()) q.pop ();
memset (vis,0,sizeof (vis));
memset (dist,0x7f,sizeof (dist));
dist[s] = 0,vis[s] = 1,q.push (s);
while (!q.empty()){
int u = q.front();q.pop ();vis[u] = 0;
for (Int i = head[u];i;i = nxt[i]){
int v = to[i],w = wei[i];
if (dist[v] > dist[u] + w){
dist[v] = dist[u] + w;
if (!vis[v]) vis[v] = 1,q.push (v);
}
}
}
for (Int i = 1;i <= m;++ i) if (dist[to[i]] == dist[st[i]] + wei[i]) ins[i] = 1;else ins[i] = 0;
} int que[MAXN],deg[MAXN],ans[MAXN],cnt1[MAXN],cnt2[MAXN];
void Topo (int S){
memset (deg,0,sizeof (deg));
memset (cnt1,0,sizeof (cnt1));
memset (cnt2,0,sizeof (cnt2));
int tot = 0;cnt1[S] = 1;
for (Int i = 1;i <= m;++ i) if (ins[i]) deg[to[i]] ++;
queue <int> q;while (!q.empty()) q.pop();q.push (S);
while (!q.empty()){
int u = q.front();q.pop (),que[++ tot] = u;
for (Int i = head[u];i;i = nxt[i]){
if (!ins[i]) continue;
(cnt1[to[i]] += cnt1[u]) %= mod;
if (!(-- deg[to[i]])) q.push (to[i]);
}
}
for (Int k = tot;k >= 1;-- k){
int u = que[k];cnt2[u] ++;
for (Int i = head[u];i;i = nxt[i]){
if (!ins[i]) continue;
(cnt2[u] += cnt2[to[i]]) %= mod;
}
}
} void Solve (int S){
SPFA (S),Topo (S);
for (Int i = 1;i <= m;++ i) if (ins[i]) (ans[i] += 1ll * cnt1[st[i]] * cnt2[to[i]] % mod) %= mod;
} signed main(){
read (n,m);
for (Int i = 1,u,v,w;i <= m;++ i) read (u,v,w),Add_Edge (u,v,w);
for (Int S = 1;S <= n;++ S) Solve (S);
for (Int i = 1;i <= m;++ i) write (ans[i]),putchar ('\n');
return 0;
}

最新文章

  1. 使用GITHUB的体会
  2. 通过Nginx实现负载均衡
  3. R语言学习笔记:因子
  4. storm环境搭建
  5. centos6.4 安装 hive 0.12.0
  6. POJ 1995
  7. mysqldump 的一些使用参数
  8. yii2定义模版
  9. swift 关于闭包和函数
  10. BZOJ 1876: [SDOI2009]SuperGCD( 更相减损 + 高精度 )
  11. PAT (Advanced Level) 1061. Dating (20)
  12. [Upper case conversion ] 每个单词的首小写字母转换为对应的大写字母
  13. 浅析java程序的执行过程
  14. 初见Hadoop—- 搭建MyEclipse 访问HDFS 上的文件
  15. Activity的生命周期函数
  16. Salt初识和安装
  17. 使用X.509数字证书加密解密实务(一)-- 证书的获得和管理
  18. mybatis 中between and用法
  19. SQLite日期时间函数
  20. 区别JS中类的静态方法,静态变量,实例方法,实例变量

热门文章

  1. Linux的基础——虚拟机的克隆
  2. Spring Boot +Vue 项目实战笔记(三):数据库的引入
  3. CPU内部结构域寄存器
  4. Javascirpt 面向对象总结-公有/私有
  5. JDK、JRE、JVM的基本介绍
  6. mybaits源码分析--binding模块(五)
  7. 操作系统IO之零拷贝技术
  8. 聚类算法与K-means实现
  9. css文本溢出省略号大总结,如你所愿
  10. 软件测试2021:第一次作业——热身练习(Bug)