bzoj 2750 [HAOI2012]Road 拓扑排序+最短路

题面

题目传送门

解法

又是最短路(DAG)呢……

  • 暴力还是挺显然的,直接枚举起点和终点,然后做一遍最短路就可以了
  • 然而复杂度就爆炸了,应该是 O(n2(n+m)logm) O ( n 2 ( n + m ) log ⁡ m )
  • 我们不妨枚举起点,假设为 S S
  • 因为所有边均为正权,所以我们可以把所有可能出现在最短路上的边存下来,建出一个新图,显然这个新图一定是一个DAG
  • 观察一下这个新图有什么性质,可以发现,从起点任意走一条路径一定是起点到这个点的最短路
  • 那么,问题就被我们转化成某条边被有多少条从起点出发的路径经过,这个正反两遍拓扑排序就可以了
  • 时间复杂度:O(n(n+m)logm+nm)

【注意事项】

  • 原图是一个有向图……我一开始以为是无向图,然后样例其实也是满足的,然后就WA上天了

代码

#include <bits/stdc++.h>
#define Mod 1000000007
#define N 2000
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {
    int next, num, v, id;
} e[N * 10];
int n, m, cnt, in[N], dis[N], used[N], ans[N * 4], sum[2][N];
vector <int> E[2][N], id[2][N];
void add(int x, int y, int v, int id) {
    e[++cnt] = (Edge) {e[x].next, y, v, id};
    e[x].next = cnt;
}
void spfa(int s) {
    for (int i = 1; i <= n; i++)
        dis[i] = INT_MAX, used[i] = 0;
    dis[s] = 0; queue <int> q; q.push(s);
    vector <pair <int, int> > tmp[N];
    while (!q.empty()) {
        int x = q.front(); q.pop(); used[x] = 0;
        for (int p = e[x].next; p; p = e[p].next) {
            int k = e[p].num, v = e[p].v;
            if (dis[k] > dis[x] + v) {
                dis[k] = dis[x] + v, tmp[k].clear();
                tmp[k].push_back(make_pair(x, e[p].id));
                if (!used[k]) q.push(k), used[k] = 1;
            } else if (dis[k] == dis[x] + v) tmp[k].push_back(make_pair(x, e[p].id));
        }
    }
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < tmp[i].size(); j++) {
            pair <int, int> x = tmp[i][j];
            int k = x.first, y = x.second;
            E[0][k].push_back(i), E[1][i].push_back(k);
            id[0][k].push_back(y), id[1][i].push_back(y);
        }
}
void topsort(int key) {
    for (int i = 1; i <= n; i++) sum[key][i] = in[i] = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < E[key][i].size(); j++) {
            int k = E[key][i][j];
            in[k]++;
        }
    queue <int> q;
    for (int i = 1; i <= n; i++) {
        if (key) sum[key][i] = 1;
        if (!in[i]) q.push(i), sum[key][i] = 1;
    }
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = 0; i < E[key][x].size(); i++) {
            int k = E[key][x][i]; in[k]--;
            sum[key][k] += sum[key][x];
            if (!in[k]) q.push(k);
        }
    }
}
int main() {
    read(n), read(m); cnt = n;
    for (int i = 1; i <= m; i++) {
        int x, y, v; read(x), read(y), read(v);
        add(x, y, v, i);
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++)
            E[0][j].clear(), E[1][j].clear(), id[0][j].clear(), id[1][j].clear();
        spfa(i); topsort(0); topsort(1);
        for (int j = 1; j <= n; j++)
            for (int k = 0; k < E[0][j].size(); k++) {
                int x = id[0][j][k], y = E[0][j][k];
                ans[x] = (ans[x] + 1ll * sum[0][j] * sum[1][y]) % Mod;
            }
    }
    for (int i = 1; i <= m; i++) cout << ans[i] << "\n";
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值