CSU 1804

1804: 有向无环图

            Time Limit: 5 Sec       Memory Limit: 128 Mb       Submitted: 732       Solved: 305    

Description

Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v,不存在从点 v 开始、点 v 结束的路径)。
为了方便,点用 1,2,…,n 编号。 设 count(x,y) 表示点 x 到点 y 不同的路径数量(规定 count(x,x)=0),Bobo 想知道

除以 (10 9+7) 的余数。
其中,a i,b j 是给定的数列。

Input

输入包含不超过 15 组数据。
每组数据的第一行包含两个整数 n,m (1≤n,m≤10 5).
接下来 n 行的第 i 行包含两个整数 a i,b i (0≤a i,b i≤10 9).
最后 m 行的第 i 行包含两个整数 u i,v i,代表一条从点 u i 到 v i 的边 (1≤u i,v i≤n)。

Output

对于每组数据,输出一个整数表示要求的值。

Sample Input

3 3
1 1
1 1
1 1
1 2
1 3
2 3
2 2
1 0
0 2
1 2
1 2
2 1
500000000 0
0 500000000
1 2

Sample Output

4
4
250000014
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define MOD 1000000007
#define MAX 100005
//数组开的太大了,我用的是codeblocks导致一直运行不了,后来发现数组开大了,定义为全局变量就好,
//全局变量是存储在数据区的,而局部变量是用栈存储的,栈太小,会溢出
using namespace std;
vector<int> list[MAX];
long long a[MAX],b[MAX];
int degree[MAX];
long long sum[MAX];
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        memset(degree,0,sizeof(degree));
        memset(list,0,sizeof(list));
        memset(sum,0,sizeof(sum));
        for(int i=1; i<=n; i++)
        {
            scanf("%lld%lld",&a[i],&b[i]);
            sum[i]=b[i];
        }
        for(int i=1; i<=m; i++)
        {
            int fa,son;
            scanf("%d%d",&fa,&son);
            degree[fa]++;//记录从fa出发的路径数
            list[son].push_back(fa);//记录到son的点
        }
        queue<int> original;
        queue<int> sorted;

        for(int i=1; i<=n; i++)
        {
            if(degree[i]==0)
                original.push(i);//没有出度的入队
        }
        while(!original.empty())//队列不为空
        {
            int son=original.front();//读取最底端元素
            original.pop();//出队(先进先出)取出最底端元素
            for(int i=0; i<list[son].size(); i++)
            {
                int fa=list[son][i];
                degree[fa]--;
                if(degree[fa]==0)
                    original.push(fa);
            }
            sorted.push(son);
        }
        long long ans=0;

        while(!sorted.empty())//队列不为空
        {
            int son=sorted.front();
            sorted.pop();
            /*eg:4 4
                 1 1
                 1 1
                 1 1
                 1 1
                 1 2
                 1 3
                 2 3
                 1 4
                 出队的顺序是3 4 2 1
                 这样子的话首先计算的是以3为终点的路径,分别是1->3,2->3
                 1->3:ans=ans+a[1]*b[3]=1;b[1]=b[1]+b[3]=2;
                 2->3:ans=ans+a[2]*b[3]=2;b[2]=b[2]+b[3]=2;
                 接着就是
                 1->4:ans=ans+a[1]*b[4]=3;b[1]=b[1]+b[4]=3;
                 然后
                 1->2:ans=ans+a[1]*b[2]=5;b[1]=b[1]+b[2]=5;
                 从数据可以看出算1->2的时候其实是这样子算的,首先加上原来累加的,其次b[2]=b[2]+b[3],这样是因为1->2->3
                 也可以这样子认为1->3的时候只计算了一次b[3],到1->2的时候要多加一个b[3],这个地方一开始想错了,以至于钻了好久的牛角尖,
                 还是得自己慢慢领悟吧!
            */
            for(int i=0; i<list[son].size(); i++)
            {
                int fa=list[son][i];
                ans=(ans+(a[fa]*sum[son])%MOD)%MOD;
                sum[fa]=(sum[fa]+sum[son])%MOD;
            }
        }
        printf("%lld\n",ans%MOD);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值