[CSU 1804 有向无环图] DP+拓扑排序

[CSU 1804 有向无环图] DP+拓扑排序

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

i=1nj=1ncount(i,j)aibj
除以 (109+7) 的余数。
其中, ai , bj 是给定的数列。
解题思路:这个题目想清楚了真的是一个大水题。但是还是很有灵活性的。可惜省赛的时候脑子不好使。
细细观察上面的式子,对于图中每个点(假设该点为 t ),设为p1,p2,,pk为点 t 的直接前驱点,我们可以用cntt表示 i=ni=1count(i,t)bi ,显然, cntt 可以由点 t 的所有直接前驱节点的结果得到。可以写出状态转移方程如下:
cntt=i=1k(cntpi+api)
最后,对于所有的点 i(1in) ,乘上对应的 bi ,然后求和就得到答案了。

#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;

const int MAXN = 1e5 + 5;
const int MOD = 1e9 + 7;

struct Edge {
    int v, next;
    Edge() {}
    Edge(int v, int next) : v(v), next(next) {}
} edges[MAXN];
int N, M;
int head[MAXN], ESZ, IN[MAXN];
LL A[MAXN], B[MAXN], cnt[MAXN];
queue<int> Q;

void init() {
    ESZ = 0;
    memset(head, -1, sizeof(head));
    memset(IN, 0, sizeof(IN));
    memset(cnt, 0, sizeof(cnt));
}
void add_edge(int u, int v) {
    edges[ESZ] = Edge(v, head[u]);
    head[u] = ESZ++;
}
void topu() {
    int u, v;
    while(!Q.empty()) Q.pop();
    for (u = 1; u <= N; u++) {
        if(!IN[u]) Q.push(u);
    }
    while(!Q.empty()) {
        u = Q.front();
        Q.pop();
        for (int i = head[u]; ~i; i = edges[i].next) {
            v = edges[i].v;
            cnt[v] = (cnt[v] + cnt[u] + A[u]) % MOD;
            IN[v] --;
            if(!IN[v]) Q.push(v);
        }
    }
}
int main() {
//    freopen("input.txt", "r", stdin);
    int u, v;
    LL ans;
    while(~scanf("%d %d", &N, &M)) {
        init();
        for(int i = 1; i <= N; i++) {
            scanf("%lld %lld", &A[i], &B[i]);
        }
        for(int i = 1; i <= M; i++) {
            scanf("%d %d", &u, &v);
            add_edge(u, v);
            IN[v] ++;
        }
        topu();
        ans = 0;
        for (int i = 1; i <= N; i++) {
            ans = (ans + cnt[i] * B[i] % MOD) % MOD;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值