[HAOI2012]ROAD 经过每条边的最短路条数+最短路+拓扑DP
题目
[HAOI2012]ROAD (https://ac.nowcoder.com/acm/problem/19987)
题目描述
C国有n座城市,城市之间通过m条单向道路连接。一条路径被称为最短路,当且仅当不存在从它的起点到终点的另外一条路径总长度比它小。两条最短路不同,当且仅当它们包含的道路序列不同。我们需要对每条道路的重要性进行评估,评估方式为计算有多少条不同的最短路经过该道路。现在,这个任务交给了你。
输入描述:
第一行包含两个正整数n、m
接下来m行每行包含三个正整数u、v、w,表示有一条从u到v长度为w的道路
输出描述:
输出应有m行,第i行包含一个数,代表经过第i条道路的最短路的数目对1000000007取模后的结果
n≤1500、m≤5000、w≤10000
输入
4 4
1 2 5
2 3 5
3 4 5
1 4 8
输出
2
3
2
1
思路
这题的实际上是求
输出k=1~m
我们枚举起点,跑最短路得到它到其他所有点的最短网络。边u-v存在最短路上当且仅当
现在我们得到一个DGA。对于一条边x->y在当前起点S下经过它的次数a[x]*b[y];
a[x]:S到点x的路径条数。b[y]:y到其他所有点的路径条数。
a[x]和b[x]都可以在DGA上DP就可以了。
a[x]:通过拓扑序DP。a[to]+=a[u]
b[x]:通过dfs记忆化DP。b[u]+=b[to]
#include <bits/stdc++.h>
#define LL long long
#define pii pair<LL, int>
using namespace std;
const LL mod=1000000007;
struct Edge {
int from, to, w;
int nxt;
int id;
};
//s-所有点的有向最短路图(DGA 可拓扑)
Edge E[200050];
int head[200050], d[200050], cut=0;
struct Digraph {
Edge e[200050];
int vis[200050], head[200050], cut=0;
LL dis[200050];
void init() {//图不变,多次换s跑
memset(vis, 0, sizeof(vis));
memset(dis, 0x3f, sizeof(dis));
//memset(head, 0, sizeof(head));
//cut=0;
}
void Addedge(int x, int y, LL w) {
e[++cut]= {x, y, w, head[x]};
head[x]=cut;
}
priority_queue<pii> q;
void bfs(int s) {
q.push({dis[s]=0, s});
while(!q.empty()) {
pii now=q.top();
q.pop();
int u=now.second;
if(vis[u])
continue;
vis[u]=1;
for(int i=head[u]; i; i=e[i].nxt) {
int to=e[i].to;
LL w=e[i].w;
if(dis[to]>dis[u]+w) {
dis[to]=dis[u]+w;
q.push({-dis[to], to});
}
}
}
}
} T;
void get() {//得到最短路网络DGA
memset(head, 0, sizeof(head));
memset(d, 0, sizeof(d));
cut=0;
for(int i=1; i<=T.cut; i++) {
int x=T.e[i].from, y=T.e[i].to;
LL w=T.e[i].w;
if(T.dis[x]+w==T.dis[y]) {
E[++cut]= {x, y, w, head[x], i};
head[x]=cut;
d[y]++;
}
}
}
LL a[200050], b[200050], f[200050];
queue<int> q;
void bfs(int u) {//通过拓扑序DP得到a[]
a[u]=1;
q.push(u);
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=head[u]; i; i=E[i].nxt) {
int to=E[i].to;
a[to]=(a[to]+a[u])%mod;
if(--d[to]==0) {
q.push(to);
}
}
}
}
void dfs(int u) {//通过dfs记忆化DP得到b[]
if(b[u])
return ;
for(int i=head[u]; i; i=E[i].nxt) {
int to=E[i].to;
dfs(to);
b[u]=(b[u]+b[to])%mod;
}
b[u]++;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
T.init();
for(int i=1; i<=m; i++) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
T.Addedge(x, y, z);
}
for(int i=1; i<=n; i++) {//枚举起点
T.init();
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
T.bfs(i);
get();
bfs(i);
dfs(i);
for(int i=1; i<=cut; i++) {//每条边的贡献
int x=E[i].from, y=E[i].to;
f[E[i].id]=(f[E[i].id]+a[x]*b[y]%mod)%mod;
}
}
for(int i=1; i<=m; i++) {
printf("%lld\n", f[i]);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)