【bzoj2750】【HAOI2012】【Road】【最短路+dp】

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

HINT

数据规模

30%的数据满足:n≤15、m≤30

60%的数据满足:n≤300、m≤1000

100%的数据满足:n≤1500、m≤5000、w≤10000

题解:

把每个点当作起点做spfa。

每次都处理两个数组a[i],b[i]分别表示从起点到点i的最短路数,和从i出发的最短路数。

然后枚举每条边,如果这条边在当前最短路图中。设这条边从i指向j,那就把a[i]*b[j]累加入这条边的答案。

对于这两个数组。我们可以在最短路图上dp一下,或者叫它dfs。

首先b数组比较好做。直接用它的后继结点更新即可.

对于a数组.我们需要先处理一个pre数组.pre[i]表示在最短路图中有多少条边指向i。

然后在做a数组的过程中.我们显然要正着算。

对于一个点,只有它被所有指向它的边的起点更新过之后,再去用这个点更新其他点。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define P 1000000007
#define N 2000
#define M 5010
using namespace std;
struct use{int st,en,v;}e[M];
int point[N],next[M],x,y,v,n,m,cnt,p[N],dis[N],l[N*100];
long long ans[M],a[N],b[N];
bool f[N];
void add(int x,int y,int v){
    next[++cnt]=point[x];point[x]=cnt;
    e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;
}
int spfa(int x){  
    int h(0),t(1),u;  
    memset(dis,127/3,sizeof(dis));memset(f,false,sizeof(f));
    dis[x]=0;l[t]=x;f[x]=true;  
    while (h<t){  
      u=l[++h];f[u]=false;  
      for (int i=point[u];i;i=next[i])  
        if (dis[e[i].en]>dis[u]+e[i].v){  
          dis[e[i].en]=dis[u]+e[i].v;  
          if (!f[e[i].en]){f[e[i].en]=true;l[++t]=e[i].en;  }  
        }  
    }    
}  
void dfs1(int x){
   f[x]=true;
   for (int i=point[x];i;i=next[i]){
      if (dis[e[i].en]==dis[x]+e[i].v){
         p[e[i].en]++;if (!f[e[i].en]) dfs1(e[i].en);
      }
   }
}
void dfs2(int x){
  for (int i=point[x];i;i=next[i]){
     if (dis[e[i].en]==dis[x]+e[i].v){
       p[e[i].en]--;(a[e[i].en]+=a[x])%=P;if (!p[e[i].en]) dfs2(e[i].en);
      }
    }
}
void dfs3(int x){
    b[x]=1;
    for (int i=point[x];i;i=next[i]){
     if (dis[e[i].en]==dis[x]+e[i].v){
       if (!b[e[i].en]) dfs3(e[i].en);(b[x]+=b[e[i].en])%=P;                                                                    
     }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++){scanf("%d%d%d",&x,&y,&v);add(x,y,v);}
    for (int i=1;i<=n;i++){
        spfa(i);memset(a,0,sizeof(a));memset(b,0,sizeof(b));memset(f,0,sizeof(f));
        dfs1(i);a[i]=1;dfs2(i);dfs3(i);
        for (int j=1;j<=m;j++){
          if (dis[e[j].en]==dis[e[j].st]+e[j].v) (ans[j]+=a[e[j].st]*b[e[j].en])%=P;
        }
    }
  for (int i=1;i<=m;i++){printf("%lld\n",ans[i]);}
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值