ABC319 G - Counting Shortest Paths

問題概要

$N$ 頂点の完全グラフから $M$ 辺を取り除いたとき、頂点 $1$ から頂点 $N$ への最短経路の個数を求めよ。
・ $2 \le N \le 2 \times 10^5$
・ $0 \le M \le \min(2 \times 10^5, \binom{N}{2})$
575 点

解説

全頂点に対して頂点 $1$ からの最短距離が分かれば、距離が小さい順に最短経路の個数を決定できる。
頂点 $1$ からBFS をする。このとき、頂点から出ている辺をすべて見るのではなく、未訪問の頂点のうち、辺が存在するものに遷移すれば $O(N)$ 回の遷移で済む。
頂点 $1$ からの最短距離が分かれば、最短距離で頂点をソートして区間加算・一点取得を高速にできるデータ構造を用いて、最短距離が $k$ の頂点から最短距離が $k+1$ の頂点に遷移することで計算できる。加算をする区間は、取得する位置より後にあるので、いもす法を適用して $O(N+M)$ で計算できる。
全体の計算量は $O(M+ N \log{N})$ 。

サンプル 1 が表現しているグラフ
サンプル $1$
サンプル 2 が表現しているグラフ
サンプル $2$
(5/14 追記) ソートはバケットソートを用いることで $O(N)$ でできるため、全体の計算量は $O(N+M)$ にすることができる。

実装上の注意

出力する答えが $0$ の時に $-1$ を出力する実装にしていると、答えが $998244353$ の倍数の時にも $-1$ を出力してしまうので、距離の配列などを使って頂点 $N$ まで到達できるかどうかを判定しておくとよい。

AC コード

#include <bits/stdc++.h>
using namespace std;

using ll = long long;
#define rep(i, n) for (ll i=0; i<(ll)(n); i++)
const ll mod = 998244353;
const int inf = numeric_limits<int>::max()/2;
const ll INF = numeric_limits<ll>::max()/2;

void solve() {
    int n, m; cin >> n >> m;
    unordered_set<ll> edges;
    vector<vector<int>> g(n);
    rep(i, m) {
        ll u, v; cin >> u >> v; u--; v--;
        edges.insert(u * n + v);
        edges.insert(v * n + u);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    vector<pair<int, int>> dist(n); dist[0] = {0, 0};
    rep(i, n-1) dist[i+1] = {inf, i+1};
    unordered_set<int> unvisited; rep(i, n-1) unvisited.insert(i + 1);
    queue<int> q; q.push(0);
    while (!q.empty()) {
        auto x = q.front(); q.pop();
        vector<int> visited;
        for (auto& y : unvisited) {
            if (!edges.count(1LL * x * n + y)) {
                visited.emplace_back(y);
                dist[y].first = dist[x].first + 1;
                q.push(y);
            }
        }
        for (auto& y : visited) {
            unvisited.erase(y);
        }
    }
    if (dist[n-1].first == inf) {
        cout << -1 << '\n';
        return;
    }
    sort(dist.begin(), dist.end());
    vector<int> ind(n); rep(i, n) ind[dist[i].second] = i;
    vector<ll> cnt(n+1); cnt[0] = 1; cnt[1] = -1;
    int maxdist = 0; rep(i, n) if (dist[i].first != inf) maxdist = dist[i].first;
    vector<pair<int, int>> sec(maxdist+2, {n, n}); sec[0] = {0, 0};
    rep(i, n) {
        if (i==n-1 || dist[i].first != dist[i+1].first) {
            if (dist[i].first != inf) sec[dist[i].first].second = i+1;
            if (i != n-1 && dist[i+1].first != inf) sec[dist[i+1].first].first = i+1;
        }
    }
    rep(i, n) {
        if (dist[i].first == inf) break;
        int I = dist[i].second;
        cnt[sec[dist[i].first+1].first] += cnt[i];
        cnt[sec[dist[i].first+1].first] %= mod;
        cnt[sec[dist[i].first+1].second] -= cnt[i];
        cnt[sec[dist[i].first+1].second] %= mod;
        for (auto& J : g[I]) {
            int j = ind[J];
            if (dist[i].first + 1 == dist[j].first) {
                cnt[j] -= cnt[i];
                cnt[j] %= mod;
                cnt[j+1] += cnt[i];
                cnt[j+1] %= mod;
            }
        }
        cnt[i+1] += cnt[i];
        cnt[i+1] %= mod;
    }
    cout << (cnt[ind[n-1]]%mod+mod)%mod << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T = 1;
    // cin >> T;
    while (T--) solve();
    return 0;
}

コメント

このブログの人気の投稿

[自作問題] yukicoder contest 494 オムニバス G - No.3479 Regions by Random Points

ABC320 G - Slot Strategy 2 (Hard)