luogu2505
luogu2505 [HAOI2012]道路
1 题目描述
C国有n座城市,城市之间通过m条[b]单向[/b]道路连接。一条路径被称为最短路,当且仅当不存在从它的起点到终点的另外一条路径总长度比它小。两条最短路不同,当且仅当它们包含的道路序列不同。我们需要对每条道路的重要性进行评估,评估方式为计算有多少条不同的最短路经过该道路。现在,这个任务交给了你。
2 数据范围
100%的数据满足:n≤1500、m≤5000、w≤10000。
3 思路
如果我们确定一个起点,那么可以根据单源最短路径构造出一个有向图无环图(一定是无环的,为什么)。然后如果没有出现这个DAG里面的边,是不可能出现在起点到这些点的最短路径上的。所以我们只要考虑这个DAG上的边就可以了。 做两次拓扑,第一次从起点开始,\(num1[i]\)表示从起点到i有几种走法。第二次逆向拓扑,\(num2[i]\)表示从其他点倒走到i有几种可能性。 那么一条边(x,y)作为最短路径上的边的可能性就是\(num1[x]*num2[y]\)。
总的时间复杂度:\(O(nm)\),这里用spfa来求解单源最短路径,没有菊花图,时间效率还挺高的。也可以用堆优化的dijkstra来做,这样时间复杂度就是\(O(n\times mlog n)\)。
4 代码
#include<bits/stdc++.h>
using namespace std;
int const N=1500+3;
int const M=5000+3;
int const mod=1e9+7;
int const inf=1e9;
struct edge{
int to,nt,w,id;
}e[M],e2[M],e3[M];
int n,m,h[N],cnt,num1[N],num2[N],ans[M],q[N*N],vis[N],d[N],h2[N],cnt2,ind[N],cnt3,h3[N];
void add(int a,int b,int c,int d){
e[++cnt].to=b;
e[cnt].nt=h[a];
e[cnt].w=c;
e[cnt].id=d;
h[a]=cnt;
}
void add2(int a,int b,int c,int d){
e2[++cnt2].to=b;
e2[cnt2].nt=h2[a];
e2[cnt2].w=c;
e2[cnt2].id=d;
h2[a]=cnt2;
}
void add3(int a,int b,int c,int d){
e3[++cnt3].to=b;
e3[cnt3].nt=h3[a];
e3[cnt3].w=c;
e3[cnt3].id=d;
h3[a]=cnt3;
}
void topology_sort(int s){
memset(num1,0,sizeof(num1));
memset(num2,0,sizeof(num2));
cnt3=0;
memset(h3,0,sizeof(h3));
int l=0,r=0;q[0]=s; num1[s]=1;
while (l<=r){
int x=q[l++];
for(int i=h2[x];i;i=e2[i].nt){
int v=e2[i].to;
ind[v]--;
num1[v]=(num1[v]+num1[x])%mod;
add3(v,x,e2[i].w,e2[i].id);
if(ind[v]==0)
q[++r]=v;
}
}
memset(ind,0,sizeof(ind));
for(int i=1;i<=n;i++)
for(int j=h3[i];j;j=e3[j].nt){
int v=e3[j].to;
ind[v]++;
}
l=0,r=-1;
for(int i=1;i<=n;i++)
if(ind[i]==0) q[++r]=i;
for(int i=1;i<=n;i++) num2[i]=1;
while (l<=r) {
int x=q[l++];
for(int i=h3[x];i;i=e3[i].nt){
int v=e3[i].to;
ind[v]--;
num2[v]=(num2[v]+num2[x])%mod;
if(ind[v]==0)
q[++r]=v;
}
}
for(int i=1;i<=n;i++)
for(int j=h2[i];j;j=e2[j].nt){
int v=e2[j].to;
ans[e2[j].id]=(ans[e2[j].id]+1LL*num1[i]*num2[v])%mod;
}
}
void solve(int s){
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++) d[i]=inf;
d[s]=0;
int l=0,r=0;
q[0]=s;
vis[s]=1;
while (l<=r){
int x=q[l++];
vis[x]=0;
for(int i=h[x];i;i=e[i].nt){
int v=e[i].to;
if(d[v]>d[x]+e[i].w){
d[v]=d[x]+e[i].w;
if(!vis[v]){
vis[v]=1;
q[++r]=v;
}
}
}
}
cnt2=0;
memset(h2,0,sizeof(h2));
memset(ind,0,sizeof(ind));
for(int i=1;i<=n;i++)
for(int j=h[i];j;j=e[j].nt){
int v=e[j].to;
if(d[v]==d[i]+e[j].w)
add2(i,v,e[j].w,e[j].id),ind[v]++;
}
topology_sort(s);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z,i);
}
for(int i=1;i<=n;i++)
solve(i);
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}