2019牛客国庆集训派对day7 B- 有向无环图(BFS)

题干:

链接:https://ac.nowcoder.com/acm/contest/1112/B
来源:牛客网

Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v,不存在从点 v 开始、点 v 结束的路径)。
为了方便,点用 1,2,…,n 编号。设 c o u n t ( x , y ) c o u n t ( x , y ) \mathrm{count}(x, y)count(x,y) count(x,y)count(x,y) 表示点 x 到点 y 不同的路径数量(规定 c o u n t ( x , x ) = 0 \mathrm{count}(x, x) = 0 count(x,x)=0),Bobo 想知道链
∑ i = 1 n ∑ j = 1 n c o u n t ( i , j ) ⋅ a i ⋅ b j \sum_{i=1}^{n}\sum_ {j=1}^{n}count(i,j)⋅a i⋅b j i=1nj=1ncount(i,j)aibj,除以 ( 1 0 9 10^9 109+7)的余数。其中, a i , b j a_i, b_j ai,bj是给定的数列。

输入包含不超过 15 组数据。
每组数据的第一行包含两个整数 n, m ( 1 ≤ n , m ≤ 1 0 5 1\leq n, m \leq 10^5 1n,m105 ).
接下来 n 行的第 i 行包含两个整数 a i , b i a_i, b_i ai,bi( 0 ≤ a i , b i ≤ 1 0 9 0 \leq a_i, b_i \leq 10^9 0ai,bi109)
最后 m 行的第 i 行包含两个整数 u i , v i u_i, v_i ui,vi ,代表一条从点 u i 到 v i u_i到 v_i uivi的边 ( 1 ≤ u i , v i ≤ n 1 \leq u_i, v_i \leq n 1ui,vin)。
对于每组数据,输出一个整数表示要求的值。

输入:3 3
1 1
1 1
1 1
1 2
1 3
2 3
输出:4
输入:2 2
1 0
0 2
1 2
1 2
输出:4
输入:2 1
500000000 0
0 500000000
1 2
输出:250000014

思路:

根据 a ∗ b + c ∗ b = ( a + c ) ∗ b a*b+c*b=(a+c)*b ab+cb=(a+c)b这一性质,如果存在类似x->y->z这种路, a [ x ] ∗ b [ y ] + a [ x ] b [ z ] + a [ y ] b [ z ] a[x]*b[y]+a[x]b[z]+a[y]b[z] a[x]b[y]+a[x]b[z]+a[y]b[z],可以写成 a [ x ] ∗ b [ y ] + ( a [ x ] + a [ y ] ) ∗ b [ z ] a[x]*b[y]+(a[x]+a[y])*b[z] a[x]b[y]+(a[x]+a[y])b[z]即x->z这条路可以在求y->z时求出。
这样我们把起点(即入度为0的点)入队开始搜索,找到他的所有出度,并使出度点的a[v]加上入度的点a[u],然后如果当前出度点的入度为0了,就把它也入队。
因为每次只搜一段,所有不同的路径都会被搜到。
然后为了防止意外用了快速乘。

#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
typedef long long ll;
ll a[100010],b[100010],in[100010];
vector<int> v[100010];
queue<int> q;
ll ksc(ll x,ll y){
    ll res=0;
    while(y){
        if(y&1)
			res=(res+x)%mod;
        x=(x<<1)%mod; 
		y>>=1;
    }
	return res;
}
int main()
{
	int n,m,x,y;
	while(scanf("%d%d",&n,&m)!=EOF){
		for(int i=0;i<=n;i++)
			v[i].clear();
		memset(in,0,sizeof(in));
		long long ans=0;
		for(int i=1;i<=n;i++){
			scanf("%lld%lld",&a[i],&b[i]);
		}
		for(int i=1;i<=m;i++){
			scanf("%d%d",&x,&y);
			v[x].push_back(y);
			in[y]++;
		}
		for(int i=1;i<=n;i++){
			if(!in[i])
				q.push(i);
		}
		while(!q.empty()){
			int u=q.front();
			q.pop();
			for(int i=0;i<v[u].size();i++){
				int v1=v[u][i];
				ans=(ans+ksc(a[u],b[v1]))%mod;
				a[v1]=(a[v1]+a[u])%mod;
				in[v1]--;
				if(!in[v1]){
					q.push(v1);
				}
			}
		}
		printf("%lld\n",ans);
	}
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值