P2505 [HAOI2012]道路
统计每条边被最短路经过几次,点数不大,考虑计算以每个点为起点时对其他边的贡献
对于某个点 $S$ 为起点的贡献,首先跑一遍最短路,建出最短路的 $DAG$
考虑 $DAG$ 上的某条边被以 $S$ 为起点的最短路经过的方案数,设此边为 $(u,v)$ ,那么方案数就是 $S$ 到 $u$ 的方案数,乘上 $v$ 到后面各点的方案数
$S$ 到 $u$ 的方案数可以按拓扑序 $dp$ 一遍得到,$v$ 到后面各点的方案数可以建反图再跑一遍 $dp$
然后就可以计算起点 $S$ 对各条边的贡献,对每个点作为起点分别计算贡献即可
具体实现看代码,挺简单的
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<vector> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2007,M=1e4+7,mo=1e9+7; inline int fk(int x) { return x>=mo ? x-mo : x; } int n,m; ll ans[M]; int fir[N],from[M<<1],to[M<<1],val[M<<1],id[M<<1],cntt; inline void add(int a,int b,int c,int d) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; val[cntt]=c; id[cntt]=d; } int dis[N]; struct dat { int x,d; dat (int a=0,int b=0) { x=a,d=b; } inline bool operator < (const dat &tmp) const { return d>tmp.d; } }; priority_queue <dat> Q; void Dijk(int S)//求以S为起点到各个点的最短路 { for(int i=1;i<=n;i++) dis[i]=mo; Q.push(dat(S,0)); dis[S]=0; while(!Q.empty()) { dat x=Q.top(); Q.pop(); if(dis[x.x]!=x.d) continue; for(int i=fir[x.x];i;i=from[i]) { int &v=to[i]; if(dis[v]<=x.d+val[i]) continue; dis[v]=x.d+val[i]; Q.push(dat(v,dis[v])); } } } vector <int> V[N],G[N];//V存DAG int du[N],f[N],g[N];//入度,S到各个点的方案,各个点到后面其他点的方案 void Tuopu(int *F,bool type)//DAG上dp算方案数 { queue <int> q; for(int i=1;i<=n;i++) if(!du[i]) q.push(i),F[i]=1; if(type) for(int i=1;i<=n;i++) F[i]=1; while(!q.empty()) { int x=q.front(),len=V[x].size(); q.pop(); for(int i=0;i<len;i++) { int &v=V[x][i]; F[v]=fk(F[v]+F[x]); du[v]--; if(!du[v]) q.push(v); } } } void calc(int S)//计算以S为起点的贡献 { for(int i=1;i<=n;i++) du[i]=f[i]=g[i]=0,V[i].clear(),G[i].clear(); for(int i=1;i<=n;i++) for(int j=fir[i];j;j=from[j]) { int &v=to[j]; if(dis[v]!=dis[i]+val[j]) continue; V[i].push_back(v); du[v]++; } Tuopu(f,0); for(int i=1;i<=n;i++) G[i]=V[i],V[i].clear(); for(int i=1;i<=n;i++) for(int j=G[i].size()-1;j>=0;j--) V[G[i][j]].push_back(i),du[i]++;//建反图 Tuopu(g,1); for(int i=1;i<=n;i++) for(int j=fir[i];j;j=from[j]) { int &v=to[j]; if(dis[v]!=dis[i]+val[j]) continue; ans[id[j]]=fk(ans[id[j]] + 1ll*f[i]*g[v]%mo );//注意long long } } int main() { n=read(),m=read(); int a,b,c; for(int i=1;i<=m;i++) a=read(),b=read(),c=read(),add(a,b,c,i); for(int i=1;i<=n;i++) Dijk(i),calc(i); for(int i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }