diff --git a/graph/incremental_scc/checker.cpp b/graph/incremental_scc/checker.cpp new file mode 100644 index 00000000..98391ba5 --- /dev/null +++ b/graph/incremental_scc/checker.cpp @@ -0,0 +1,62 @@ +// https://github.com/MikeMirzayanov/testlib/blob/master/checkers/wcmp.cpp + +// The MIT License (MIT) + +// Copyright (c) 2015 Mike Mirzayanov + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "testlib.h" + +using namespace std; + +int main(int argc, char * argv[]) +{ + setName("compare sequences of tokens"); + registerTestlibCmd(argc, argv); + + int n = 0; + string j, p; + + while (!ans.seekEof() && !ouf.seekEof()) + { + n++; + + ans.readWordTo(j); + ouf.readWordTo(p); + + if (j != p) + quitf(_wa, "%d%s words differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str()); + } + + if (ans.seekEof() && ouf.seekEof()) + { + if (n == 1) + quitf(_ok, "\"%s\"", compress(j).c_str()); + else + quitf(_ok, "%d tokens", n); + } + else + { + if (ans.seekEof()) + quitf(_wa, "Participant output contains extra tokens"); + else + quitf(_wa, "Unexpected EOF in the participants output"); + } +} diff --git a/graph/incremental_scc/gen/example_00.in b/graph/incremental_scc/gen/example_00.in new file mode 100644 index 00000000..96383d71 --- /dev/null +++ b/graph/incremental_scc/gen/example_00.in @@ -0,0 +1,8 @@ +4 6 +1 1 1 1 +0 1 +1 2 +2 0 +2 3 +1 3 +3 0 diff --git a/graph/incremental_scc/gen/example_01.in b/graph/incremental_scc/gen/example_01.in new file mode 100644 index 00000000..2e2ac2ca --- /dev/null +++ b/graph/incremental_scc/gen/example_01.in @@ -0,0 +1,8 @@ +4 6 +12 34 56 78 +0 1 +1 2 +2 0 +2 3 +1 3 +3 0 diff --git a/graph/incremental_scc/gen/example_02.in b/graph/incremental_scc/gen/example_02.in new file mode 100644 index 00000000..9b23a737 --- /dev/null +++ b/graph/incremental_scc/gen/example_02.in @@ -0,0 +1,9 @@ +2 7 +12 34 +0 0 +1 1 +0 0 +0 1 +1 1 +0 1 +1 0 diff --git a/graph/incremental_scc/gen/large_cycle.cpp b/graph/incremental_scc/gen/large_cycle.cpp new file mode 100644 index 00000000..ca220a09 --- /dev/null +++ b/graph/incremental_scc/gen/large_cycle.cpp @@ -0,0 +1,32 @@ +#include +#include +#include "random.h" +#include "../params.h" + +using namespace std; + +int main(int, char* argv[]) { + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = min(MAX_N, MAX_M); + int m = n; + + auto P = gen.perm(n); + printf("%d %d\n", n, m); + + // vertex weight + for (int i = 0; i < n; ++i) { + if (i) printf(" "); + int x = gen.uniform(0, MOD - 1); + printf("%d", x); + } + printf("\n"); + + for (int i = 0; i < n; i++) { + int a = P[i]; + int b = P[(i + 1) % n]; + printf("%d %d\n", a, b); + } + return 0; +} diff --git a/graph/incremental_scc/gen/max_random.cpp b/graph/incremental_scc/gen/max_random.cpp new file mode 100644 index 00000000..a749c89c --- /dev/null +++ b/graph/incremental_scc/gen/max_random.cpp @@ -0,0 +1,31 @@ +#include +#include + +#include "random.h" +#include "../params.h" + +using namespace std; + +int main(int, char* argv[]) { + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = MAX_N; + int m = MAX_M; + printf("%d %d\n", n, m); + + // vertex weight + for (int i = 0; i < n; ++i) { + if (i) printf(" "); + int x = gen.uniform(0, MOD - 1); + printf("%d", x); + } + printf("\n"); + + for (int i = 0; i < m; i++) { + int a = gen.uniform(0, n - 1); + int b = gen.uniform(0, n - 1); + printf("%d %d\n", a, b); + } + return 0; +} diff --git a/graph/incremental_scc/gen/random.cpp b/graph/incremental_scc/gen/random.cpp new file mode 100644 index 00000000..5eb97ec1 --- /dev/null +++ b/graph/incremental_scc/gen/random.cpp @@ -0,0 +1,30 @@ +#include +#include +#include "random.h" +#include "../params.h" + +using namespace std; + +int main(int, char* argv[]) { + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = gen.uniform(MIN_N, MAX_N); + int m = MAX_M; + printf("%d %d\n", n, m); + + // vertex weight + for (int i = 0; i < n; ++i) { + if (i) printf(" "); + int x = gen.uniform(0, MOD - 1); + printf("%d", x); + } + printf("\n"); + + for (int i = 0; i < m; i++) { + int a = gen.uniform(0, n - 1); + int b = gen.uniform(0, n - 1); + printf("%d %d\n", a, b); + } + return 0; +} diff --git a/graph/incremental_scc/gen/small_random.cpp b/graph/incremental_scc/gen/small_random.cpp new file mode 100644 index 00000000..66ba4944 --- /dev/null +++ b/graph/incremental_scc/gen/small_random.cpp @@ -0,0 +1,30 @@ +#include +#include +#include "random.h" +#include "../params.h" + +using namespace std; + +int main(int, char* argv[]) { + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = seed % 10 + 1; + int m = gen.uniform(1, 10); + printf("%d %d\n", n, m); + + // vertex weight + for (int i = 0; i < n; ++i) { + if (i) printf(" "); + int x = gen.uniform(0, MOD - 1); + printf("%d", x); + } + printf("\n"); + + for (int i = 0; i < m; i++) { + int a = gen.uniform(0, n - 1); + int b = gen.uniform(0, n - 1); + printf("%d %d\n", a, b); + } + return 0; +} diff --git a/graph/incremental_scc/hash.json b/graph/incremental_scc/hash.json new file mode 100644 index 00000000..1027e61f --- /dev/null +++ b/graph/incremental_scc/hash.json @@ -0,0 +1,48 @@ +{ + "example_00.in": "ce1e2b7602de696ff21326aefd223cc72e8bcd7cbb0d09d52c05dbce197b7234", + "example_00.out": "8edd978abd87c151acd43955c5264061370b7e7b98e5f5fb550c0c19e8363bc4", + "example_01.in": "9c48912e2fce6b36c5838847bb9d575819ecbe22ec6891051bb3767900bca6d1", + "example_01.out": "4baab6663da6839f8ee133d14a15f812bcbb86c3af3c5b7d1110817daa3b72e0", + "example_02.in": "0e9db62c07014c2c6a13354ab2a7a08a88271517ae5fb777e0c149f8d4625c09", + "example_02.out": "69f81fcdbdf0f04204bcadd26fb42c84047e142832501213c5418273742ec2c4", + "large_cycle_00.in": "61868820df363a231d88e50ff233beff0a1c8b88f706068875c5617e96c36494", + "large_cycle_00.out": "2b314be0d030e0522141c0914edbfedcfdf778487a709c071792977aff7e7127", + "large_cycle_01.in": "e5f49503c07d452778f9a71c22e37175ba804ec763fc02d76330dcaeac5e2001", + "large_cycle_01.out": "889b8bfce4cfec56759bf45d35f02efbac9375c50cebdd0a4e79ab7d04588c80", + "max_random_00.in": "79d9907fb20c59d34f73eca46e77b4a31c692e5454198d0060dbef0bc3c5fadd", + "max_random_00.out": "680265ac73345337c825bc12fc6b1d421f91d6e5d5f2a74b209a4939e4145fad", + "max_random_01.in": "56954cf7ca6571a6a35f8235866339698525f305bc7cdb5bd1761d26710dedce", + "max_random_01.out": "3a96780dff2671f579f9a33a3a02297fdffba21edbbd05aa9b5393f011375956", + "max_random_02.in": "4165bd80d26bf0c5512f7b2246d54675d500c500606f6e057e5dd4efb6ae9673", + "max_random_02.out": "6664920cf97488b98bd864ef12f3e15b80d49709b9ea80b47bb149a0ef3cc64e", + "random_00.in": "17d38bd5df7e60bc416c14faa501f81f3bbf1e0c954b1dc5f15b06c407ad5dc9", + "random_00.out": "51b0c7faad65f647886dc4c43430dc791e9bf3e843cb66ae71a0977bc3655c5c", + "random_01.in": "3f037437c567691376c679ab615051946e81f7d5ff0eae084b272c0b54c603b0", + "random_01.out": "be522379ab9ee6c2b26109250ce0d68fe9202d8fe1fa481739760e7e31ddeb3c", + "random_02.in": "f231e31f20ab1721d2df51b0d68988b321c861dafaf87246f5eacc176b2a00f0", + "random_02.out": "f88d28b31584c79dc3974f0287dc66cbb7568c5bdb35cedfa28bad0e1d12b56d", + "random_03.in": "aed3d8161170186ac9c31950833a113fc35edd28dc905c906bd1fd2d3b09b14f", + "random_03.out": "163bcbc65f9e2cd649cd3f6270eaac71f49e9bf1e50f22f17b32f78d64211bf5", + "random_04.in": "504c86a79e8ffcd067e2c98b13e1f455d1700203a343d13400eb8c954e4d6eb9", + "random_04.out": "e2e0316317f0abd13381231255aed51be568babce10545c8011098af7487b808", + "random_05.in": "13149f106b73f4581ddb936d2be69f21cb15d8a1dfd6305c68e635381f32707e", + "random_05.out": "760d6f54b6428e9f79c5d5e8014298e02ce920c341bb848e5ee60427e14d6fef", + "random_06.in": "2232c393a715b32732a716df81879aedf4e6f593314cdfd607196039340fb391", + "random_06.out": "7005a07c24f5dc4637b45dab08190f3098527c9fc955c73a2db913e50657e7dc", + "random_07.in": "94c339538f6b5dc154917518b7484ed3640493034f7e36f6e035103289e26153", + "random_07.out": "a97c751c94764afb49c69060476d12853ef2996b9eac1f0fb4cfd2b641fb7248", + "random_08.in": "282015462a7ed0be72440ba12e29dc929f5ad31af2070cc3977d001d5c7b397f", + "random_08.out": "025a24e0e96091ad07e08c4898f97a86e83a19598bf60a4843b73958770c87b3", + "random_09.in": "82f0ad324f50b9ef098d0ec7e6c50dff32c2e8f367c8045e0ae4af615534a03e", + "random_09.out": "e32f4e6929440c80e550073a8464fbc872fd312455ff58bf27e16f9fd18ce10c", + "small_random_00.in": "3cae2c3b1394e4a29d1c53ac756b23030568163e89ca0f323a43dc52cc6fd060", + "small_random_00.out": "aeec6cd696078c273d36ddb33500d5af1aeac51b1b65a725363f3ae5728dd8c6", + "small_random_01.in": "aced3c218d30cf507ee538e490604711b1dd561fd3f38968587c0a74e8dcb581", + "small_random_01.out": "acb88cc45a983fc5559854d1193217b31aa4efbbd52b0bf154ab0873194cf7a9", + "small_random_02.in": "a75f4ffc0ae19330359f68742382b47706de22ce0ef7c0c172e6b9b3ef3d956b", + "small_random_02.out": "2de84622a56b51c09e96feabbe375cb7b1fdb1502810076b130475a40a7dc5a1", + "small_random_03.in": "ed8574b10b6932208ad32a925bea8b12dae213ad98958fc2b35dc5678fbade26", + "small_random_03.out": "9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa", + "small_random_04.in": "1e5057a776b4235636fac9d5a0811df94c67144276fe410ad6c57b197f66d33b", + "small_random_04.out": "6a33a504c8d16194914401f4f46532de96e1b63119fc5981341c6b65c6c27096" +} \ No newline at end of file diff --git a/graph/incremental_scc/info.toml b/graph/incremental_scc/info.toml new file mode 100644 index 00000000..8dd91ad5 --- /dev/null +++ b/graph/incremental_scc/info.toml @@ -0,0 +1,30 @@ +title = 'Strongly Connected Components (Incremental)' +timelimit = 5.0 +forum = "https://github.com/yosupo06/library-checker-problems/issues/1192" + +[[tests]] + name = "example.in" + number = 3 +[[tests]] + name = "small_random.cpp" + number = 5 +[[tests]] + name = "random.cpp" + number = 10 +[[tests]] + name = "max_random.cpp" + number = 3 +[[tests]] + name = "large_cycle.cpp" + number = 2 + +[[solutions]] + name = "naive.cpp" + allow_tle = true + +[params] + MOD = 998_244_353 + MIN_N = 1 + MAX_N = 500_000 + MIN_M = 1 + MAX_M = 500_000 diff --git a/graph/incremental_scc/sol/correct.cpp b/graph/incremental_scc/sol/correct.cpp new file mode 100644 index 00000000..5b2f5a9b --- /dev/null +++ b/graph/incremental_scc/sol/correct.cpp @@ -0,0 +1,317 @@ +#include +#include +#include +#include +#include +#include + +using namespace std; + +using ll = long long; +using u32 = unsigned int; +using u64 = unsigned long long; + +template +using vc = vector; + +template +constexpr T infty = 0; +template <> +constexpr int infty = 1'000'000'000; + +#define FOR1(a) for (ll _ = 0; _ < ll(a); ++_) +#define FOR2(i, a) for (ll i = 0; i < ll(a); ++i) +#define FOR3(i, a, b) for (ll i = a; i < ll(b); ++i) +#define FOR4(i, a, b, c) for (ll i = a; i < ll(b); i += (c)) +#define FOR1_R(a) for (ll i = (a)-1; i >= ll(0); --i) +#define FOR2_R(i, a) for (ll i = (a)-1; i >= ll(0); --i) +#define FOR3_R(i, a, b) for (ll i = (b)-1; i >= ll(a); --i) +#define overload4(a, b, c, d, e, ...) e +#define overload3(a, b, c, d, ...) d +#define FOR(...) overload4(__VA_ARGS__, FOR4, FOR3, FOR2, FOR1)(__VA_ARGS__) +#define FOR_R(...) overload3(__VA_ARGS__, FOR3_R, FOR2_R, FOR1_R)(__VA_ARGS__) + +#define all(x) x.begin(), x.end() +#define len(x) ll(x.size()) +#define elif else if + +#define eb emplace_back +#define mp make_pair +#define fi first +#define se second + +template +T POP(vc &que) { + T a = que.back(); + que.pop_back(); + return a; +} + +template +inline bool chmax(T &a, const S &b) { + return (a < b ? a = b, 1 : 0); +} +template +inline bool chmin(T &a, const S &b) { + return (a > b ? a = b, 1 : 0); +} + +template +struct Edge { + int frm, to; + T cost; + int id; +}; + +template +struct Graph { + static constexpr bool is_directed = directed; + int N, M; + using cost_type = T; + using edge_type = Edge; + vector edges; + vector indptr; + vector csr_edges; + bool prepared; + + class OutgoingEdges { + public: + OutgoingEdges(const Graph *G, int l, int r) : G(G), l(l), r(r) {} + + const edge_type *begin() const { + if (l == r) { return 0; } + return &G->csr_edges[l]; + } + + const edge_type *end() const { + if (l == r) { return 0; } + return &G->csr_edges[r]; + } + + private: + const Graph *G; + int l, r; + }; + + bool is_prepared() { return prepared; } + + Graph() : N(0), M(0), prepared(0) {} + Graph(int N) : N(N), M(0), prepared(0) {} + + void build(int n) { + N = n, M = 0; + prepared = 0; + edges.clear(); + indptr.clear(); + csr_edges.clear(); + } + + void add(int frm, int to, T cost = 1, int i = -1) { + assert(!prepared); + assert(0 <= frm && 0 <= to && to < N); + if (i == -1) i = M; + auto e = edge_type({frm, to, cost, i}); + edges.eb(e); + ++M; + } + + void build() { + assert(!prepared); + prepared = true; + indptr.assign(N + 1, 0); + for (auto &&e: edges) { + indptr[e.frm + 1]++; + if (!directed) indptr[e.to + 1]++; + } + for (int v = 0; v < N; ++v) { indptr[v + 1] += indptr[v]; } + auto counter = indptr; + csr_edges.resize(indptr.back() + 1); + for (auto &&e: edges) { + csr_edges[counter[e.frm]++] = e; + if (!directed) + csr_edges[counter[e.to]++] = edge_type({e.to, e.frm, e.cost, e.id}); + } + } + + OutgoingEdges operator[](int v) const { + assert(prepared); + return {this, indptr[v], indptr[v + 1]}; + } +}; + +template +struct modint { + static constexpr u32 umod = u32(mod); + static_assert(umod < u32(1) << 31); + u32 val; + + constexpr modint() : val(0) {} + constexpr modint(int x) : val((x %= mod) < 0 ? x + mod : x){}; + modint &operator+=(const modint &p) { + if ((val += p.val) >= umod) val -= umod; + return *this; + } + modint &operator*=(const modint &p) { + val = u64(val) * p.val % umod; + return *this; + } + modint operator+(const modint &p) const { return modint(*this) += p; } + modint operator*(const modint &p) const { return modint(*this) *= p; } +}; + +using modint107 = modint<1000000007>; +using modint998 = modint<998244353>; + +template +pair> strongly_connected_component(GT &G) { + static_assert(GT::is_directed); + assert(G.is_prepared()); + int N = G.N; + int C = 0; + vc comp(N), low(N), ord(N, -1), path; + int now = 0; + + auto dfs = [&](auto &dfs, int v) -> void { + low[v] = ord[v] = now++; + path.eb(v); + for (auto &&[frm, to, cost, id]: G[v]) { + if (ord[to] == -1) { + dfs(dfs, to), chmin(low[v], low[to]); + } else { + chmin(low[v], ord[to]); + } + } + if (low[v] == ord[v]) { + while (1) { + int u = POP(path); + ord[u] = N, comp[u] = C; + if (u == v) break; + } + ++C; + } + }; + FOR(v, N) { + if (ord[v] == -1) dfs(dfs, v); + } + FOR(v, N) comp[v] = C - 1 - comp[v]; + return {C, comp}; +} + +template +vc incremental_scc(GT &G) { + static_assert(GT::is_directed); + int N = G.N, M = G.M; + vc merge_time(M, infty); + vc> dat; + FOR(i, M) { + auto &e = G.edges[i]; + dat.eb(i, e.frm, e.to); + } + + vc new_idx(N, -1); + // L 時点ではサイクルには含まれず, R 時点では含まれる + auto dfs + = [&](auto &dfs, vc> &dat, int L, int R) -> void { + if (dat.empty() || R == L + 1) return; + int M = (L + R) / 2; + int n = 0; + for (auto &[i, a, b]: dat) { + if (new_idx[a] == -1) new_idx[a] = n++; + if (new_idx[b] == -1) new_idx[b] = n++; + } + + Graph G(n); + for (auto &[i, a, b]: dat) { + if (i < M) G.add(new_idx[a], new_idx[b]); + } + G.build(); + auto [nc, comp] = strongly_connected_component(G); + vc> dat1, dat2; + for (auto [i, a, b]: dat) { + a = new_idx[a], b = new_idx[b]; + if (i < M) { + if (comp[a] == comp[b]) { + chmin(merge_time[i], M), dat1.eb(i, a, b); + } else { + dat2.eb(i, comp[a], comp[b]); + } + } else { + dat2.eb(i, comp[a], comp[b]); + } + } + for (auto &[i, a, b]: dat) new_idx[a] = new_idx[b] = -1; + dfs(dfs, dat1, L, M), dfs(dfs, dat2, M, R); + }; + dfs(dfs, dat, 0, M + 1); + return merge_time; +} + +struct UnionFind { + int n, n_comp; + vc dat; // par or (-size) + UnionFind(int n = 0) { build(n); } + + void build(int m) { + n = m, n_comp = m; + dat.assign(n, -1); + } + int operator[](int x) { + while (dat[x] >= 0) { + int pp = dat[dat[x]]; + if (pp < 0) { return dat[x]; } + x = dat[x] = pp; + } + return x; + } + bool merge(int x, int y) { + x = (*this)[x], y = (*this)[y]; + if (x == y) return false; + if (-dat[x] < -dat[y]) swap(x, y); + dat[x] += dat[y], dat[y] = x, n_comp--; + return true; + } +}; + +using mint = modint998; + +void solve() { + int N, M; + scanf("%d %d", &N, &M); + + vc X(N); + FOR(i, N) { + int x; + scanf("%d", &x); + X[i] = x; + } + + Graph G(N); + FOR(M) { + int a, b; + scanf("%d %d", &a, &b); + G.add(a, b); + } + + auto time = incremental_scc(G); + vc> IDS(M + 1); + FOR(i, M) { + if (time[i] != infty) IDS[time[i]].eb(i); + } + + UnionFind uf(N); + mint ANS = 0; + FOR(t, 1, M + 1) { + for (auto &i: IDS[t]) { + int a = G.edges[i].frm; + int b = G.edges[i].to; + a = uf[a], b = uf[b]; + if (a == b) continue; + ANS += X[a] * X[b]; + uf.merge(a, b); + X[uf[a]] = X[a] + X[b]; + } + printf("%d\n", ANS.val); + } +} + +signed main() { solve(); } diff --git a/graph/incremental_scc/sol/naive.cpp b/graph/incremental_scc/sol/naive.cpp new file mode 100644 index 00000000..530b9056 --- /dev/null +++ b/graph/incremental_scc/sol/naive.cpp @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include +#include + +using namespace std; + +using ll = long long; +using u32 = unsigned int; +using u64 = unsigned long long; + +template +using vc = vector; + +#define FOR1(a) for (ll _ = 0; _ < ll(a); ++_) +#define FOR2(i, a) for (ll i = 0; i < ll(a); ++i) +#define FOR3(i, a, b) for (ll i = a; i < ll(b); ++i) +#define FOR4(i, a, b, c) for (ll i = a; i < ll(b); i += (c)) +#define FOR1_R(a) for (ll i = (a)-1; i >= ll(0); --i) +#define FOR2_R(i, a) for (ll i = (a)-1; i >= ll(0); --i) +#define FOR3_R(i, a, b) for (ll i = (b)-1; i >= ll(a); --i) +#define overload4(a, b, c, d, e, ...) e +#define overload3(a, b, c, d, ...) d +#define FOR(...) overload4(__VA_ARGS__, FOR4, FOR3, FOR2, FOR1)(__VA_ARGS__) +#define FOR_R(...) overload3(__VA_ARGS__, FOR3_R, FOR2_R, FOR1_R)(__VA_ARGS__) + +#define all(x) x.begin(), x.end() +#define len(x) ll(x.size()) +#define elif else if + +#define eb emplace_back +#define fi first +#define se second + +template +T POP(vc &que) { + T a = que.back(); + que.pop_back(); + return a; +} + +template +inline bool chmax(T &a, const S &b) { + return (a < b ? a = b, 1 : 0); +} +template +inline bool chmin(T &a, const S &b) { + return (a > b ? a = b, 1 : 0); +} + +template +struct Edge { + int frm, to; + T cost; + int id; +}; + +template +struct Graph { + static constexpr bool is_directed = directed; + int N, M; + using cost_type = T; + using edge_type = Edge; + vector edges; + vector indptr; + vector csr_edges; + bool prepared; + + class OutgoingEdges { + public: + OutgoingEdges(const Graph *G, int l, int r) : G(G), l(l), r(r) {} + + const edge_type *begin() const { + if (l == r) { return 0; } + return &G->csr_edges[l]; + } + + const edge_type *end() const { + if (l == r) { return 0; } + return &G->csr_edges[r]; + } + + private: + const Graph *G; + int l, r; + }; + + bool is_prepared() { return prepared; } + + Graph() : N(0), M(0), prepared(0) {} + Graph(int N) : N(N), M(0), prepared(0) {} + + void build(int n) { + N = n, M = 0; + prepared = 0; + edges.clear(); + indptr.clear(); + csr_edges.clear(); + } + + void add(int frm, int to, T cost = 1, int i = -1) { + assert(!prepared); + assert(0 <= frm && 0 <= to && to < N); + if (i == -1) i = M; + auto e = edge_type({frm, to, cost, i}); + edges.eb(e); + ++M; + } + + void build() { + assert(!prepared); + prepared = true; + indptr.assign(N + 1, 0); + for (auto &&e: edges) { + indptr[e.frm + 1]++; + if (!directed) indptr[e.to + 1]++; + } + for (int v = 0; v < N; ++v) { indptr[v + 1] += indptr[v]; } + auto counter = indptr; + csr_edges.resize(indptr.back() + 1); + for (auto &&e: edges) { + csr_edges[counter[e.frm]++] = e; + if (!directed) + csr_edges[counter[e.to]++] = edge_type({e.to, e.frm, e.cost, e.id}); + } + } + + OutgoingEdges operator[](int v) const { + assert(prepared); + return {this, indptr[v], indptr[v + 1]}; + } +}; + +template +struct modint { + static constexpr u32 umod = u32(mod); + static_assert(umod < u32(1) << 31); + u32 val; + + static modint raw(u32 v) { + modint x; + x.val = v; + return x; + } + constexpr modint() : val(0) {} + constexpr modint(int x) : val((x %= mod) < 0 ? x + mod : x){}; + modint &operator+=(const modint &p) { + if ((val += p.val) >= umod) val -= umod; + return *this; + } + modint &operator*=(const modint &p) { + val = u64(val) * p.val % umod; + return *this; + } + modint operator+(const modint &p) const { return modint(*this) += p; } + modint operator*(const modint &p) const { return modint(*this) *= p; } +}; + +using modint107 = modint<1000000007>; +using modint998 = modint<998244353>; + +template +pair> strongly_connected_component(GT &G) { + static_assert(GT::is_directed); + assert(G.is_prepared()); + int N = G.N; + int C = 0; + vc comp(N), low(N), ord(N, -1), path; + int now = 0; + + auto dfs = [&](auto &dfs, int v) -> void { + low[v] = ord[v] = now++; + path.eb(v); + for (auto &&[frm, to, cost, id]: G[v]) { + if (ord[to] == -1) { + dfs(dfs, to), chmin(low[v], low[to]); + } else { + chmin(low[v], ord[to]); + } + } + if (low[v] == ord[v]) { + while (1) { + int u = POP(path); + ord[u] = N, comp[u] = C; + if (u == v) break; + } + ++C; + } + }; + FOR(v, N) { + if (ord[v] == -1) dfs(dfs, v); + } + FOR(v, N) comp[v] = C - 1 - comp[v]; + return {C, comp}; +} + +using mint = modint998; + +void solve() { + int N, M; + scanf("%d %d", &N, &M); + vc X(N); + FOR(i, N) { + int x; + scanf("%d", &x); + X[i] = x; + } + vc> edge; + FOR(M) { + int a, b; + scanf("%d %d", &a, &b); + edge.eb(a, b); + } + + FOR(n, 1, M + 1) { + Graph G(N); + FOR(i, n) { + auto [a, b] = edge[i]; + G.add(a, b); + } + G.build(); + auto [nc, comp] = strongly_connected_component(G); + mint ANS = 0; + FOR(b, N) FOR(a, b) { + if (comp[a] == comp[b]) ANS += X[a] * X[b]; + } + printf("%d\n", ANS.val); + } +} + +signed main() { solve(); } diff --git a/graph/incremental_scc/task.md b/graph/incremental_scc/task.md new file mode 100644 index 00000000..fb98ad25 --- /dev/null +++ b/graph/incremental_scc/task.md @@ -0,0 +1,50 @@ +## @{keyword.statement} + +@{lang.en} + +There is a $N$ vertices and $0$ edges directed graph $G$, and an integer sequence $x_0, \ldots, x_{N-1}$. + +Add $M$ directed edges to $G$. The $i$-th edge is directed from $a_i$ to $b_i$. + +After adding each edge, output the remainder when $X$ defined below modulo $@{param.MOD}$: + +- Define $\mathrm{same}(i,j)$ as $1$ if vertices $i$ and $j$ belong to the same strongly connected component in $G$, and $0$ otherwise, for $0\leq i,j \leq N-1$. +- Define $X = \sum_{0\leq i