图论①

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[];

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

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

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

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

一个b[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;                        //把起点最短路赋为,方便后面计算,即...看后面  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\n",ans[i]);      
    }
}

求推荐...

原文地址:https://www.cnblogs.com/AidenPearce/p/8536503.html

时间: 2024-08-08 05:34:54

图论①的相关文章

第九节 图论和搜索

1. 图论算法(用BFS,DFS) 拓扑排序 克隆图 找连通块 六度问题 2.BFS 队列实现:    树中的BFS与图中的BFS有什么不同?树中没有环,图中有环需要一个set来记录搜索过的节点: 应用:图的遍历,最短路径 3 搜索   搜索题的套路比较固定.

图论(A*算法,K短路) :POJ 2449 Remmarguts&#39; Date

Remmarguts' Date Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 25216   Accepted: 6882 Description "Good man never makes girls wait or breaks an appointment!" said the mandarin duck father. Softly touching his little ducks' head, h

ACM 图论入门

①图论基础 图由点和边组成 记顶点集合为V 边集合为E的图为G=(V,E) 图可分为有向图和无向图.如表示朋友关系的图为无向图,表示点之间大小关系的图为有向图. 边也可以带有权值,带有权值称为有权图,不带有权值称为 无权图. 一.关于无向图 任意两点之间都有路径的图叫做连通图,顶点连接的边数称为这个点的度. 没有环的连通图就是树,没有环的非连通图就是森林. 一棵树的边数=顶点数-1.反之 边数=顶点数-1的连通图就是树. 二.关于有向图 以一个点为起点的边数称作这个点的出度,以一个点为终点的边数

Codeforces 444A DZY Loves Physics(图论)

题目链接:Codeforces 444A DZY Loves Physics 题目大意:给出一张图,图中的每个节点,每条边都有一个权值,现在有从中挑出一张子图,要求子图联通,并且被选中的任意两点,如果存在边,则一定要被选中.问说点的权值和/边的权值和最大是多少. 解题思路:是图论中的一个结论,最多两个节点,所以枚举两条边就可以了.我简单的推了一下,2个点的情况肯定比3个点的优. 假设有3个点a,b,c,权值分别为A,B,C 现a-b,b-c边的权值分别为u,v 那么对于两点的情况有A+Bu,B+

图论算法之DFS与BFS

概述(总) DFS是算法中图论部分中最基本的算法之一.对于算法入门者而言,这是一个必须掌握的基本算法.它的算法思想可以运用在很多地方,利用它可以解决很多实际问题,但是深入掌握其原理是我们灵活运用它的关键所在. 含义特点 DFS即深度优先搜索,有点类似广度优先搜索,也是对一个连通图进行遍历的算法.它的思想是从一个顶点V0开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节点,然后从另一条路开始走到底,这种尽量往深处走的概念即是深度优先的概念. 由于用到递归,当节点特别多且深度很大

codevs——1019 集合论与图论

1019 集合论与图论 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 集合论与图论对于小松来说是比数字逻辑轻松,比数据结构难的一门专业必修课.虽然小松在高中的时候已经自学过了离散数学中的图论,组合,群论等知识.但对于集合论,小松还是比较陌生的.集合论的好多东西也涉及到了图论的知识. 在第四讲的学习中,小松学到了“有序对”这么一个概念,即用<x, y>表示有序对x和y.要注意的是有序对<x, y>

关于图论的若干巴拉巴拉

最近课堂上正在讲图论 先安利MIT课程:http://open.163.com/special/opencourse/algorithms.html 因为本人对图论的概念并不是很清楚,所以还是整理一下吧. 1.图论的基本概念 几种常见的图的分类: 类型 边 允许多重边 允许环 简单图 无向 否 否 多重图 无向 是 否 伪图 无向 是 是 有向图 有向 否 是 有向多重图 有向 是 是 完全图:n个顶点上的完全图是在每对不同顶点之间都恰有一条边的简单图. 二分图:若把简单图G的顶点集合分为两个不

cdoj1580 简单图论问题

地址:http://acm.uestc.edu.cn/#/problem/show/1580 题目: 简单图论问题 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 给出一个无向图,该图由nn个点和mm条边组成,每个点和每条边都有一个权值.对于该图的任意一个子图,我们定义A是该子图的点权和,B是该子图的边权和,C=A/b=AB是该子图的powerpow

SDUT 3361 数据结构实验之图论四:迷宫探索

数据结构实验之图论四:迷宫探索 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Discuss Problem Description 有一个地下迷宫,它的通道都是直的,而通道所有交叉点(包括通道的端点)上都有一盏灯和一个开关:请问如何从某个起点开始在迷宫中点亮所有的灯并回到起点? Input 连续T组数据输入,每组数据第一行给出三个正整数,分别表示地下迷宫的结点数N(1 < N <= 1000).边数M(M <= 30

SDUT 2141 【TEST】数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历

数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Discuss Problem Description 给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列.(同一个结点的同层邻接点,节点编号小的优先遍历) Input 输入第一行为整数n(0< n <100),表示数据的组数.对于每组数据,第一行是三个整数k,m,t(0<