[最短路+拓扑]BZOJ 2750 [HAOI2012]Road

题目大意

给出一个有向图,求有向图上每条边被多少不同的最短路通过。

n ≤ 1500 , e ≤ 5000 n\le1500,e\le5000 n1500,e5000

解题分析

签到题?反正并不难,就对于每个点先求最短路,然后取出所有 d s t [ x ] + w [ j ] = = d s d [ s o n [ j ] ] dst[x]+w[j]==dsd[son[j]] dst[x]+w[j]==dsd[son[j]]对跑出来的图进行拓扑排序,正着做一遍求出从起点到达点 u u u的最短路方案 s x [ u ] sx[u] sx[u],倒着做一遍求出以点 v v v为起点的最短路方案 s y [ v ] sy[v] sy[v]。一条边在这张图里的贡献就是 s x [ u ] ∗ s y [ v ] sx[u]*sy[v] sx[u]sy[v]

示例代码

题目传送门

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int tt=1000000007,maxn=1505,maxe=5005;
int n,e,tot,ans[maxe],son[maxe],w[maxe],nxt[maxe],lnk[maxn];
int dst[maxn],que[maxn],qu[maxn],in[maxn],sx[maxn],sy[maxn];
bool vs[maxn],vis[maxe];
void _add(int x,int y,int z){son[++tot]=y; w[tot]=z; nxt[tot]=lnk[x]; lnk[x]=tot;}
void _tuopu(int s){
	memset(vis,0,sizeof(vis));
	for (int i=1;i<=n;i++)
		for (int j=lnk[i];j;j=nxt[j]) in[son[j]]+=(vis[j]=(dst[i]+w[j]==dst[son[j]]));
	for (int i=1;i<=n;i++) {sx[i]=0; sy[i]=1;}
	int hed=0,til=1; qu[1]=s; sx[s]=1;
	while (hed<til)
		for (int j=lnk[qu[++hed]];j;j=nxt[j])
			if (vis[j]&&!(--in[son[j]])) qu[++til]=son[j];
	for (int i=1;i<=til;i++)
		for (int j=lnk[qu[i]];j;j=nxt[j])
			if (vis[j]) sx[son[j]]+=sx[qu[i]];
	for (int i=til;i;i--)
		for (int j=lnk[qu[i]];j;j=nxt[j])
			if (vis[j]) sy[qu[i]]+=sy[son[j]];
	for (int i=1;i<=n;i++)
		for (int j=lnk[i];j;j=nxt[j])
			if (vis[j]) ans[j]=(ans[j]+(LL)sx[i]*sy[son[j]]%tt)%tt;
}
void _spfa(int s){
	memset(vs,0,sizeof(vs)); 
	memset(in,0,sizeof(in));
	memset(dst,63,sizeof(dst));
	int hed=0,til=1; que[1]=s; dst[s]=0; vs[s]=1;
	while (hed!=til){
		hed=(hed+1)%maxn; vs[que[hed]]=0;
		for (int j=lnk[que[hed]];j;j=nxt[j])
			if (dst[son[j]]>dst[que[hed]]+w[j]){
				dst[son[j]]=dst[que[hed]]+w[j];
				if (!vs[son[j]]){
					til=(til+1)%maxn; que[til]=son[j]; vs[son[j]]=1;
					if (dst[son[j]]>dst[(hed+1)%maxn]) swap(que[til],que[(hed+1)%maxn]);
				}
			}
	}
	_tuopu(s);
}
int main()
{
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	scanf("%d%d",&n,&e); tot=0;
	for (int i=1,x,y,z;i<=e;i++){scanf("%d%d%d",&x,&y,&z); _add(x,y,z);}
	for (int i=1;i<=n;i++) _spfa(i);
	for (int i=1;i<=e;i++) printf("%d\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值