BZOJ2750&&洛谷P2505 [HAOI2012]道路

毒瘤题

拓扑+SPFA+暴力枚举

枚举起点,跑SPFA求出dis[i]

然后每进行完一次SPFA,就枚举最短路图中的所有边--->满足dis[fr]+cost=dis[to]的边就是最短路上的边,把这些边标记起来,构成一张最短路图,显然这张图上不存在环,因为不可能存在一条路径满足u->v->z->u,显然我们可以拓扑统计到达每个点的方案数

tot[to]+=tot[fr]

然后我们对于在最短路图中的点,u->v,显然这也是一条最短路径,不然的话,必定存在u->k->v,使得原图的最短路变小,然后我们就需要统计一下每个点出发的最短路,显然从这个点出发的边,并且存在于最短路图上,那这就是一条最短路,所以cnt[fr]+=cnt[to]

最后统计一条边的贡献时,显然根据乘法原理ans[i]=tot[fr]*cnt[to],即到达这条边一个端点的最短路数*从另一个端点出去的最短路数

代码

//By AcerMo
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio> 
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=100500;
const int mod=1e9+7;
int n,m;
int tot[M],all[M],emm[M];
int dis[M],vis[M],ono[M],ans[M];
int to[M],nxt[M],w[M],head[M],fr[M],cnt;
stack<int>s;
inline int read()
{
	int x=0;char ch=getchar();
	while (ch>'9'||ch<'0') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
inline void add(int x,int y,int z)
{
	to[++cnt]=y;fr[cnt]=x;nxt[cnt]=head[x];w[cnt]=z;head[x]=cnt;
	return ;
}
inline void SPFA(int st)
{
	fill(dis,dis+n+1,2e9);
	fill(ono,ono+m+1,0);
	queue<int>q;dis[st]=0;q.push(st);
	while (q.size())
	{
		int u=q.front();q.pop();vis[u]=0;
		for (int i=head[u];i;i=nxt[i])
		if (dis[to[i]]>dis[u]+w[i])
		{
			dis[to[i]]=dis[u]+w[i];
			if (!vis[to[i]])
				vis[to[i]]=1,q.push(to[i]);
		}
	}
	for (int i=1;i<=m;i++)
	if (dis[fr[i]]+w[i]==dis[to[i]])
	ono[i]=1;//确定本次的所有可行路径 
	return ;
} 
inline void topsort(int st)
{
	fill(emm,emm+n+1,0);
	fill(tot,tot+n+1,0);
	fill(all,all+n+1,0);
	queue<int>q;tot[st]=1;q.push(st);
	for (int i=1;i<=m;i++) emm[to[i]]+=ono[i];//计算入度 
	while (q.size())
	{
		int u=q.front();q.pop();s.push(u);
		for (int i=head[u];i;i=nxt[i])
		if (ono[i])//必须是最短路图中的边 
		{
			if (--emm[to[i]]==0) q.push(to[i]);
			tot[to[i]]=(tot[to[i]]+tot[u])%mod;//拓扑统计所有的数量 
		}
	}
	while (s.size())
	{
		int u=s.top();s.pop();all[u]++;
		for (int i=head[u];i;i=nxt[i])
		if (ono[i])
			all[u]=(long long int)(all[u]+all[to[i]])%mod;
	}
	return ;
}
inline void slove(int st)
{
	SPFA(st);topsort(st);
	for (int i=1;i<=m;i++)
	if (ono[i]) ans[i]=(ans[i]+1LL*tot[fr[i]]*all[to[i]]%mod)%mod;
	return ;
}
signed main()
{
	n=read();m=read();int x,y,z;
	for (int i=1;i<=m;i++)
	x=read(),y=read(),z=read(),add(x,y,z);
	for (int i=1;i<=n;i++) slove(i);
	for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值