• 图论①——??? (2750: [HAOI2012]Road)


    2750: [HAOI2012]Road

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 651  Solved: 302
    [Submit][Status][Discuss]

    Description

    C国有n座城市,城市之间通过m条单向道路连接。一条路径被称为最短路,当且仅当不存在从它的起点到终点的另外一条路径总长度比它小。两条最短路不同,当且仅当它们包含的道路序列不同。我们需要对每条道路的重要性进行评估,评估方式为计算有多少条不同的最短路经过该道路。现在,这个任务交给了你。

    Input

    第一行包含两个正整数n、m
    接下来m行每行包含三个正整数u、v、w,表示有一条从u到v长度为w的道路

    Output

    输出应有m行,第i行包含一个数,代表经过第i条道路的最短路的数目对1000000007取模后的结果

    Sample Input

    4 4
    1 2 5
    2 3 5
    3 4 5
    1 4 8

    Sample Output

    2
    3
    2
    1

    简而言之,这道题就是求每个边在构建最短路用了多少次(有几个最短路包含这个边)

    感觉很麻烦,既然是最短路,可以先分别以每个点为起点,求出最短路数组d[i](从起点到每个点最短路的距离);

    然后考虑怎么算一个边用了几次;

    首先前提是这个边是最短路上的边

    即:d[i]+val==d[v](起点到点i的最短路加上点i到点v的路,如果等于起点到点v的最短路,则满足)

    然后可以有一个a[i]数组,记录起点到点i的最短路数;

    一个b[i]数组,记录每个点i(意义和上行i一样)到其余点的最短路数;

    然后怎么求这两个数组:

    计算肯定有一定顺序,不然可能会重复计算,

    由于与起点相连的最小边肯定是 至少一条最短路上的 边(与起点相连的最小边一定是组成最小路的边)

    原因就是 djste djksl  ....dj算法的证明(英语不好)

    然后就可以用这条边连的点进行扩展,即可求出a数组

    b数组求法同理,但要倒着找,应为后面节点(后面节点概念看下面)的最短路肯定比前面节点的最短路少;

    所谓后面节点,就是指节点被dj算法扔进堆里的顺序;

    详见代码注释;代码如下:

                                                                少女祈祷中......

    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <cstring>
     
    using namespace std;
    const int N=5010;
    const int mod=1000000007;
     
    int n,m;
    int c[N],d[N];
    bool vis[N];
    long long ans[N],a[N],b[N];
    struct data
    {
        int v,nxt,val,num;
    }edge[N];
    int cnt,alist[N];
    inline void add(int u,int v,int val,int num)
    {
        edge[++cnt].v=v;edge[cnt].val=val;edge[cnt].num=num;
        edge[cnt].nxt=alist[u];alist[u]=cnt;
    }
    struct nod
    {
        int num,val;
        friend bool operator <(nod a,nod b){return a.val>b.val;}
    };
    void dj(int s)
    {
        priority_queue<nod> q;
        memset(vis,false,sizeof vis);memset(d,0x3f3f3f3f,sizeof d);
        nod sx; sx.num=s; sx.val=0;
        d[s]=0;q.push(sx);
        int tot=0;
        while(!q.empty())
        {
            int num=q.top().num;q.pop();
            if(vis[num]) continue;
            vis[num]=true;c[++tot]=num;        //c数组记录遍历顺序,即与远点距离顺序,先记录的距离近
            int nxt=alist[num];             //num不是边的序号,是点!!!
            while(nxt)
            {
                int val=edge[nxt].val;
                int v=edge[nxt].v;
                if(d[num]+val<d[v])
                {
                    d[v]=d[num]+val;
                    nod xx;xx.num=v;xx.val=d[v];
                    q.push(xx);
                }
                nxt=edge[nxt].nxt;
            }
        }
        memset(a,0,sizeof a);memset(b,0,sizeof b);
        for(int i=1;i<=tot;++i) b[c[i]]=1;        //每个点的最短路至少有一条
        a[s]=1;                        //把起点最短路赋为1,方便后面计算,即...看后面
      
    for(int i=1;i<=tot;++i) { int nxt=alist[c[i]];              //从离起点最近的点找a数组 while(nxt) { int val=edge[nxt].val; int v=edge[nxt].v; if(d[c[i]]+val==d[v]) { a[v]+=a[c[i]];            //应为从起点扩展,起点到的满足条件(d[c[i]]+val==d[v])的边,为从他那扩展来的边的数目加他自己的数目...(感觉没解释清) a[v]%=mod; } nxt=edge[nxt].nxt; } } for(int i=tot;i;--i)                //b数组同理反向 { int nxt=alist[c[i]]; while(nxt) { int val=edge[nxt].val; int v=edge[nxt].v; if(d[c[i]]+val==d[v]) { b[c[i]]+=b[v]; b[c[i]]%=mod; } nxt=edge[nxt].nxt; } } for(int i=1;i<=n;++i)          //组合问题.... { int nxt=alist[i]; while(nxt) { int val=edge[nxt].val; int v=edge[nxt].v; if(d[i]+val==d[v]) { ans[edge[nxt].num]+=a[i]*b[v]; ans[edge[nxt].num]%=mod; } nxt=edge[nxt].nxt; } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;++i)          //i为每条边的编号 { int u,v,val; scanf("%d%d%d",&u,&v,&val); add(u,v,val,i); } for(int i=1;i<=n;++i)          //对每个节点跑最短路 { dj(i); } for(int i=1;i<=m;++i) { printf("%lld ",ans[i]);       } }

     求推荐...

  • 相关阅读:
    第一款上位机的笔记(MFC)——VS2010
    C++随笔(四)
    C++随笔(三)
    C++随笔(二)障眼法
    C++随笔
    matlab中的数据图像拟合
    在keil中调试汇编程序
    redis常见的面试题及答案
    spring定时任务的几种实现方式
    【SpringMVC】从Fastjson迁移到Jackson,以及对技术选型的反思
  • 原文地址:https://www.cnblogs.com/AidenPearce/p/8536503.html
Copyright © 2020-2023  润新知