[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

思路

这题的实际上是求i=1nj=i+1n(ij)k
输出k=1~m

我们枚举起点,跑最短路得到它到其他所有点的最短网络。边u-v存在最短路上当且仅当dis[x]+w=dis[y]
现在我们得到一个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;
}

posted @   liweihang  阅读(267)  评论(0编辑  收藏  举报
编辑推荐:
· 基于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)
Live2D
欢迎阅读『[HAOI2012]ROAD 经过每条边的最短路条数+最短路+拓扑DP』
点击右上角即可分享
微信分享提示