【HAOI2012/Luogu2505】道路 最短路DAG

原题走这里

本题的整个思想十分暴力。
首先,既然最多只有1500个点,那么就可以枚举最短路的起点,然后把最短路的条数累加到各条边上就可以了。
然而,枚举出每一条最短路再累加明显是不现实的,于是我们还需要另一个东西:最短路DAG

最短路DAG,说白了就是由一张图上所有的以某个节点为起始点的最短路构成的图。
或者更加抽象一点就是所有满足 dis[v]=dis[u]+a[u][v] d i s [ v ] = d i s [ u ] + a [ u ] [ v ] 的边 (u,v) ( u , v ) 的组成的图。
假如原图没有零环,则该图必然是DAG。
因为既然存在最短路,就不存在负环,而每走过一条边都是在从dis值低端走向dis值高的,不可能一直在上升,因此最短路图必然是DAG。

有了最短路DAG之后就好办了,因为最短路DAG中从源点S开始的所有路径都是最短路。
因此每条边 (u,v) ( u , v ) ,经过其的最短路数,为:S到u的最短路数×最短路DAG上由v开始的路径条数。
前者可以在最短路DAG按拓扑序DP求出,
后者可以按照拓扑序倒序DP求出,或者干脆记忆化搜索。

因此,我们的总体思路,就是:
枚举源点,跑SPFA求出最短路DAG,再在最短路DAG上跑两个DP。两个DP一乘,然后各条边分别累加,就AC了。
所以为什么我花了那么久。

具体实现见代码如下:

#include 
#define MOD 1000000007
using namespace std;
struct edge {
    int u,v,l,p;
} e[5010];
int n,m,top,head[1510],dis[1510],q[7500000],d[1510],w[1510],ret[5010],in[1510];
bool b[1510];
inline bool addedge(int u,int v,int l) {
    e[++top]=(edge) {u,v,l,head[u]};
    head[u]=top;
}
void SPFA(int x) {
    memset(d,0,sizeof(d));
    memset(dis,0x3f,sizeof(dis));
    memset(b,0,sizeof(b));
    register int qh=0,qt=0;
    dis[x]=0;
    q[qt++]=x;
    b[x]=1;
    w[x]=1;
    while(qhint u=q[qh++];
        b[u]=0;
        for(register int i=head[u]; i; i=e[i].p) {
            int v=e[i].v;
            if(dis[v]>dis[u]+e[i].l) {
                dis[v]=dis[u]+e[i].l;
                if(!b[v]) {
                    b[v]=1;
                    q[qt++]=v;
                }
            }
        }
    }
    for(int i=1; i<=top; i++) {
        if(dis[e[i].v]==dis[e[i].u]+e[i].l) {
            in[e[i].v]++;
        }
    }
}
void bfs(int x) {
    memset(w,0,sizeof(w));
    register int qh=0,qt=0;
    w[x]=1;
    q[qt++]=x;
    while(qhint u=q[qh++];
        for(int i=head[u]; i; i=e[i].p) {
            int v=e[i].v;
            if(dis[v]==dis[u]+e[i].l) {
                (w[v]+=w[u])%=MOD;
                if(!--in[v]) {
                    q[qt++]=v;
                }
            }
        }
    }
}
int dp(int x) {
    if(d[x])return d[x];
    d[x]=1;
    for(int i=head[x]; i; i=e[i].p) {
        if(dis[x]+e[i].l==dis[e[i].v]) {
            (d[x]+=dp(e[i].v))%=MOD;
        }
    }
    return d[x];
}
int main() {
//  freopen("roadsw.in","r",stdin);
//  freopen("roadsw.out","w",stdout);
    cin>>n>>m;
    for(int i=1; i<=m; i++) {
        int u,v,l;
        cin>>u>>v>>l;
        addedge(u,v,l);
    }
    for(int i=1; i<=n; i++) {
        SPFA(i);
        bfs(i);
        dp(i);
        for(int j=1; j<=top; j++) {
            if(dis[e[j].u]+e[j].l==dis[e[j].v]) {
                (ret[j]+=(1LL*w[e[j].u]*d[e[j].v])%MOD)%=MOD;
            }
        }
    }
    for(int i=1; i<=top; i++) {
        cout<

你可能感兴趣的:(【HAOI2012/Luogu2505】道路 最短路DAG)