zoukankan      html  css  js  c++  java
  • 【洛谷2505】[HAOI2012] 道路(最短路图)

    点此看题面

    • 给定一张(n)个点(m)条边的有向图,对每条边求出有多少条不同的最短路包含它(相同两点间不同的最短路计算多次)。
    • (nle1.5 imes10^3,mle5 imes10^3)

    最短路图

    考虑(n)这么小,我们可以直接枚举起点(s)(Dijkstra)一遍对所有点求出(dis_x)表示(s)(x)的最短路。

    那么,一条边((u,v,w))能出现在从(s)出发的最短路中,当且仅当(dis_u+w=dis_v)

    由于(dis_u+w=dis_v)的必要条件是(dis_u<dis_v),因此有用的边实际上构成一张(DAG)。(这可以看作是最短路树的进阶版本,不妨称之为最短路图。)

    容易发现,最短路图中的任意一条从(s)出发的路径都是一条最短路,且所有从(s)出发的最短路都被包含在了这张最短路图之中。

    所以我们只要拓扑排序一下,然后求出(f_u)表示从(s)(u)的路径条数,(g_v)表示从(v)到之后任意点的总路径条数。

    那么从(s)出发的最短路对于边((u,v,w))的贡献就是(f_u imes g_v)

    代码:(O(n^2logn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 1500
    #define M 5000
    #define X 1000000007
    #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
    using namespace std;
    int n,m,ee,lnk[N+5],ans[M+5];struct edge {int to,nxt,v;}e[M+5];
    typedef pair<int,int> Pr;priority_queue<Pr,vector<Pr>,greater<Pr> > Q;
    int dis[N+5],vis[N+5];I void Dij(CI s)//最短路
    {
    	RI i,k;for(i=1;i<=n;++i) dis[i]=1e9,vis[i]=0;Q.push(make_pair(dis[s]=0,s));
    	W(!Q.empty()) if(k=Q.top().second,Q.pop(),!vis[k]) for(i=lnk[k];i;i=e[i].nxt)
    		dis[k]+e[i].v<dis[e[i].to]&&(Q.push(make_pair(dis[e[i].to]=dis[k]+e[i].v,e[i].to)),0);
    }
    int deg[N+5],q[N+5],f[N+5],g[N+5];I void Solve(CI s)//拓扑求解
    {
    	RI i,j,k,H,T;for(k=1;k<=n;++k) for(i=lnk[k];i;i=e[i].nxt) dis[k]+e[i].v==dis[e[i].to]&&++deg[e[i].to];//建立最短路图
    	for(i=1;i<=n;++i) f[i]=0,g[i]=1;f[q[H=T=1]=s]=1;//初始化f,g,一开始队列中只有起点s
    	W(H<=T) for(i=lnk[k=q[H++]];i;i=e[i].nxt)//拓扑排序
    		dis[k]+e[i].v==dis[e[i].to]&&(f[e[i].to]=(f[e[i].to]+f[k])%X,!--deg[e[i].to]&&(q[++T]=e[i].to));//转移f
    	W(T) for(i=lnk[k=q[T--]];i;i=e[i].nxt)
    		dis[k]+e[i].v==dis[e[i].to]&&(g[k]=(g[k]+g[e[i].to])%X,ans[i]=(ans[i]+1LL*f[k]*g[e[i].to])%X);//转移g;更新每条边的答案
    }
    int main()
    {
    	RI i,x,y,z;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d%d",&x,&y,&z),add(x,y,z);
    	for(i=1;i<=n;++i) Dij(i),Solve(i);for(i=1;i<=m;++i) printf("%d
    ",ans[i]);return 0;//枚举起点
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    poj 3068 Bridge Across Islands
    XidianOJ 1086 Flappy v8
    XidianOJ 1036 分配宝藏
    XidianOJ 1090 爬树的V8
    XidianOJ 1088 AK后的V8
    XidianOJ 1062 Black King Bar
    XidianOJ 1091 看Dota视频的V8
    XidianOJ 1098 突击数论前的xry111
    XidianOJ 1019 自然数的秘密
    XidianOJ 1109 Too Naive
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu2505.html
Copyright © 2011-2022 走看看