From 44a5aed3687bec1cecacd14e0acfb05d6eeaa75f Mon Sep 17 00:00:00 2001 From: NyaanNyaan Date: Mon, 29 Apr 2024 15:58:10 +0900 Subject: [PATCH 1/2] add point_set_tree_path_composite_sum --- .../checker.cpp | 62 ++ .../gen/example_00.in | 6 + .../gen/example_01.in | 13 + .../gen/n_hundreds.cpp | 79 ++ .../gen/query.h | 42 + .../gen/tiny_00.in | 4 + .../gen/tiny_01.in | 12 + .../gen/typical_tree_max.cpp | 181 ++++ .../hash.json | 34 + .../info.toml | 30 + .../lib/any-direction-tree-dp.hpp | 84 ++ .../lib/csr-array.hpp | 42 + .../lib/graph.hpp | 40 + .../lib/static-modint.hpp | 42 + .../sol/correct.cpp | 887 ++++++++++++++++++ .../sol/naive.cpp | 411 ++++++++ .../point_set_tree_path_composite_sum/task.md | 124 +++ .../verifier.cpp | 142 +++ 18 files changed, 2235 insertions(+) create mode 100644 graph/point_set_tree_path_composite_sum/checker.cpp create mode 100644 graph/point_set_tree_path_composite_sum/gen/example_00.in create mode 100644 graph/point_set_tree_path_composite_sum/gen/example_01.in create mode 100644 graph/point_set_tree_path_composite_sum/gen/n_hundreds.cpp create mode 100644 graph/point_set_tree_path_composite_sum/gen/query.h create mode 100644 graph/point_set_tree_path_composite_sum/gen/tiny_00.in create mode 100644 graph/point_set_tree_path_composite_sum/gen/tiny_01.in create mode 100644 graph/point_set_tree_path_composite_sum/gen/typical_tree_max.cpp create mode 100644 graph/point_set_tree_path_composite_sum/hash.json create mode 100644 graph/point_set_tree_path_composite_sum/info.toml create mode 100644 graph/point_set_tree_path_composite_sum/lib/any-direction-tree-dp.hpp create mode 100644 graph/point_set_tree_path_composite_sum/lib/csr-array.hpp create mode 100644 graph/point_set_tree_path_composite_sum/lib/graph.hpp create mode 100644 graph/point_set_tree_path_composite_sum/lib/static-modint.hpp create mode 100644 graph/point_set_tree_path_composite_sum/sol/correct.cpp create mode 100644 graph/point_set_tree_path_composite_sum/sol/naive.cpp create mode 100644 graph/point_set_tree_path_composite_sum/task.md create mode 100644 graph/point_set_tree_path_composite_sum/verifier.cpp diff --git a/graph/point_set_tree_path_composite_sum/checker.cpp b/graph/point_set_tree_path_composite_sum/checker.cpp new file mode 100644 index 000000000..6a66d5330 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/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/point_set_tree_path_composite_sum/gen/example_00.in b/graph/point_set_tree_path_composite_sum/gen/example_00.in new file mode 100644 index 000000000..4129c09b4 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/gen/example_00.in @@ -0,0 +1,6 @@ +3 2 +1 2 3 +0 1 4 5 +1 2 6 7 +0 2 8 0 +1 0 9 10 1 diff --git a/graph/point_set_tree_path_composite_sum/gen/example_01.in b/graph/point_set_tree_path_composite_sum/gen/example_01.in new file mode 100644 index 000000000..899d5a37a --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/gen/example_01.in @@ -0,0 +1,13 @@ +8 4 +1 2 3 4 5 6 7 8 +0 1 10 1 +1 2 10 1 +0 3 10 1 +0 4 10 0 +0 5 10 1 +5 6 10 0 +6 7 10 1 +0 6 10 5 +1 4 100000 2 2 +0 7 100000 3 +0 0 100000 0 diff --git a/graph/point_set_tree_path_composite_sum/gen/n_hundreds.cpp b/graph/point_set_tree_path_composite_sum/gen/n_hundreds.cpp new file mode 100644 index 000000000..5e5ad4fc0 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/gen/n_hundreds.cpp @@ -0,0 +1,79 @@ + +#include +#include +#include +#include + +#include "../params.h" +#include "query.h" +#include "random.h" + +struct Tree { + int n = 1; + std::vector> edges; + + void shuffle_nodes(Random& rng) { + auto perm = rng.perm(n); + for (auto& e : edges) { + e.first = perm[e.first]; + e.second = perm[e.second]; + } + } + void shuffle_edges(Random& rng, bool can_flip = true) { + rng.shuffle(edges.begin(), edges.end()); + if (can_flip) { + for (auto& e : edges) { + if (rng.uniform_bool()) std::swap(e.first, e.second); + } + } + } + + void push(int u, int v) { edges.emplace_back(u, v); } + + void join_random_edges(Random& rng, int count) { + int fixn = n; + for (int i = 0; i < count; i++) { + push(rng.uniform(0, fixn - 1), n); + n++; + } + } +}; + +int main(int, char* argv[]) { + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int N = gen.uniform(100, 300); + int Q = gen.uniform(100, 300); + + Tree tree; + while (tree.n < N) { + tree.join_random_edges(gen, 1); + } + + tree.shuffle_edges(gen); + tree.shuffle_nodes(gen); + + std::vector A(N), B(N - 1), C(N - 1); + for (int i = 0; i < N; i++) A[i] = gen.uniform(A_MIN, MOD - 1); + for (int i = 0; i < N - 1; i++) B[i] = gen.uniform(B_MIN, MOD - 1); + for (int i = 0; i < N - 1; i++) C[i] = gen.uniform(C_MIN, MOD - 1); + + printf("%d %d\n", N, Q); + + for (int i = 0; i < N; i++) { + if (i) printf(" "); + printf("%d", A[i]); + } + printf("\n"); + + for (int i = 0; i < N - 1; i++) { + printf("%d %d %d %d\n", tree.edges[i].first, tree.edges[i].second, B[i], + C[i]); + } + + QueryData qd{N, Q, gen}; + qd.print(); + + return 0; +} diff --git a/graph/point_set_tree_path_composite_sum/gen/query.h b/graph/point_set_tree_path_composite_sum/gen/query.h new file mode 100644 index 000000000..16f98adf3 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/gen/query.h @@ -0,0 +1,42 @@ +// generate queries randomly + +#include +#include + +using namespace std; + +#include "../params.h" +#include "random.h" + +struct QueryData { + int N, Q; + vector cmd, I, X, Y, R; + + QueryData(int _n, int _q, Random& gen) + : N(_n), Q(_q), cmd(Q), I(Q), X(Q), Y(Q, -1), R(Q) { + for (int i = 0; i < Q; i++) { + cmd[i] = gen.uniform(0, 1); + if (N == 1) cmd[i] = 0; + if (cmd[i] == 0) { + I[i] = gen.uniform(0, N - 1); + X[i] = gen.uniform(A_MIN, MOD - 1); + R[i] = gen.uniform(0, N - 1); + } else { + I[i] = gen.uniform(0, N - 2); + X[i] = gen.uniform(B_MIN, MOD - 1); + Y[i] = gen.uniform(C_MIN, MOD - 1); + R[i] = gen.uniform(0, N - 1); + } + } + } + + void print() { + for (int i = 0; i < Q; i++) { + if (cmd[i] == 0) { + printf("%d %d %d %d\n", cmd[i], I[i], X[i], R[i]); + } else { + printf("%d %d %d %d %d\n", cmd[i], I[i], X[i], Y[i], R[i]); + } + } + } +}; diff --git a/graph/point_set_tree_path_composite_sum/gen/tiny_00.in b/graph/point_set_tree_path_composite_sum/gen/tiny_00.in new file mode 100644 index 000000000..cba986c44 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/gen/tiny_00.in @@ -0,0 +1,4 @@ +1 2 +1 +0 0 2 0 +0 0 3 0 diff --git a/graph/point_set_tree_path_composite_sum/gen/tiny_01.in b/graph/point_set_tree_path_composite_sum/gen/tiny_01.in new file mode 100644 index 000000000..ba9057ff6 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/gen/tiny_01.in @@ -0,0 +1,12 @@ +2 9 +1 100000 +0 1 3 10 +0 0 11 0 +0 1 12 1 +1 0 13 14 0 +0 0 15 1 +0 1 16 0 +1 0 17 18 1 +0 0 19 0 +0 1 20 1 +1 0 21 22 0 diff --git a/graph/point_set_tree_path_composite_sum/gen/typical_tree_max.cpp b/graph/point_set_tree_path_composite_sum/gen/typical_tree_max.cpp new file mode 100644 index 000000000..20103723d --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/gen/typical_tree_max.cpp @@ -0,0 +1,181 @@ + +#include +#include +#include +#include + +#include "../params.h" +#include "query.h" +#include "random.h" + +struct Tree { + int n = 1; + std::vector> edges; + + void shuffle_nodes(Random& rng) { + auto perm = rng.perm(n); + for (auto& e : edges) { + e.first = perm[e.first]; + e.second = perm[e.second]; + } + } + void shuffle_edges(Random& rng, bool can_flip = true) { + rng.shuffle(edges.begin(), edges.end()); + if (can_flip) { + for (auto& e : edges) { + if (rng.uniform_bool()) std::swap(e.first, e.second); + } + } + } + + void push(int u, int v) { edges.emplace_back(u, v); } + + void expand_random_edges(Random& rng, int count) { + assert(n >= 2); + int fixn = n; + for (int i = 0; i < count; i++) { + int e = rng.uniform(0, fixn - 2); + push(edges[e].first, n); + edges[e].first = n; + n++; + } + } + + void join_random_edges(Random& rng, int count) { + int fixn = n; + for (int i = 0; i < count; i++) { + push(rng.uniform(0, fixn - 1), n); + n++; + } + } + + void toCentipede() { + int fixn = n; + for (int i = 0; i < fixn; i++) { + push(i, n); + n++; + } + } + + static Tree Line(int n) { + Tree res; + res.n = n; + for (int i = 0; i < n - 1; i++) res.push(i, i + 1); + return res; + } + + static Tree Star(int n) { + Tree res; + res.n = n; + for (int i = 0; i < n - 1; i++) res.push(0, i + 1); + return res; + } + + static Tree KLines(int n, int k) { + assert(3 <= k); + Tree res; + res.n = n; + for (int i = 1; i < n; i++) res.push(std::max(0, i - k), i); + return res; + } + + static Tree KAry(int n, int k) { + assert(2 <= k); + Tree res; + res.n = n; + for (int i = 0; i < n - 1; i++) res.push(i / k, i + 1); + return res; + } + + static Tree Broom(int n, int line_length) { + assert(line_length <= n); + Tree res; + res.n = n; + for (int i = 0; i < line_length; i++) res.push(i, i + 1); + for (int i = line_length; i < n - 1; i++) res.push(line_length - 1, i + 1); + return res; + } + + static Tree LongestPathDecompositionKiller(int n) { + assert(n >= 10); + Tree res; + res.n = n; + std::vector rt; + int now_n = 0; + for (int len = 1;; len += 2) { + if (now_n + len > n) break; + for (int i = 0; i < len - 1; i++) { + res.push(now_n + i, now_n + i + 1); + } + rt.push_back(now_n); + now_n += len; + } + for (int i = 0; i + 1 < (int)rt.size(); i++) { + res.push(rt[i], rt[i + 1]); + } + for (int i = now_n; i < n; i++) { + res.push(i - 1, i); + } + return res; + } + + static Tree GenerateByTemplateId(Random& rng, int id, int n) { + assert(n >= 10); + assert(id >= 0); + if (id-- == 0) return Line(n); + if (id-- == 0) return Star(n); + if (id-- == 0) return KAry(n, 2); + if (id-- == 0) return LongestPathDecompositionKiller(n); + if (id-- == 0) { + auto res = Line(n / 2); + res.toCentipede(); + res.join_random_edges(rng, n % 2); + return res; + } + if (id-- == 0) return Broom(n, (long long)n * 2 / 5); + if (id-- == 0) return Broom(n, (long long)n * 3 / 5); + if (id-- == 0) return KLines(n, 3); + if (id-- == 0) return KAry(n, 3); + if (id-- == 0) { + auto res = Star(n / 2); + res.expand_random_edges(rng, n / 2); + return res; + } + assert(false); + } +}; + +int main(int, char* argv[]) { + int id = (int)atoll(argv[1]); + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int N = N_MAX; + int Q = Q_MAX; + + Tree tree = Tree::GenerateByTemplateId(gen, id, N); + tree.shuffle_edges(gen); + tree.shuffle_nodes(gen); + + std::vector A(N), B(N - 1), C(N - 1); + for (int i = 0; i < N; i++) A[i] = gen.uniform(A_MIN, MOD - 1); + for (int i = 0; i < N - 1; i++) B[i] = gen.uniform(B_MIN, MOD - 1); + for (int i = 0; i < N - 1; i++) C[i] = gen.uniform(C_MIN, MOD - 1); + + printf("%d %d\n", N, Q); + + for (int i = 0; i < N; i++) { + if (i) printf(" "); + printf("%d", A[i]); + } + printf("\n"); + + for (int i = 0; i < N - 1; i++) { + printf("%d %d %d %d\n", tree.edges[i].first, tree.edges[i].second, B[i], + C[i]); + } + + QueryData qd{N, Q, gen}; + qd.print(); + return 0; +} diff --git a/graph/point_set_tree_path_composite_sum/hash.json b/graph/point_set_tree_path_composite_sum/hash.json new file mode 100644 index 000000000..150692130 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/hash.json @@ -0,0 +1,34 @@ +{ + "example_00.in": "b6fa0b611f0b3507b32afa8ad2989d24e2b4baeb5fc1ccd6e8123ce1e7fdf9c2", + "example_00.out": "faa51abc248a314c2f7136dc2323a8fa6d97b8e6ba932bc313ad223bf95044f6", + "example_01.in": "71fb41d6709e82d07163a0abdc2dab275cf257a42bc6bf0725fce88e59435e09", + "example_01.out": "1e863feeb05011dc98b306863083590555c6a5c03ccb2e80ad3d16eb85951e50", + "n_hundreds_00.in": "5fc3210ce95bfbfcf49e7b23b885bdec98d8579e3bf39033cc8b8365afec694a", + "n_hundreds_00.out": "fb79473e7024ba0407b6b816c9fe873232bcd68254306650460483d12ddf5dea", + "n_hundreds_01.in": "c2d4248bffeb58d60915ee23c9aa0fa464e7b360e7d648ffc136ba990d8eec99", + "n_hundreds_01.out": "1b6c5c09dc9243ded68ddccf3949701f9b379d2c599e14c79128931224e22c71", + "tiny_00.in": "299ffde8534ba4c318513b3f6e0b750d9ad49b3287478cbe6109e79eeaf3f317", + "tiny_00.out": "fcb9cc30b0f3e4715d032f3a0ce158e4d6bea8c618bda0f5d1f167300a087b8a", + "tiny_01.in": "aa74ffcc4b07f715b28484d91014b9fff5908764f017d41d90a59d794a26149f", + "tiny_01.out": "82176668dc0b754a8da74c0b09fd643d5cedf72fde79a3c585a01bf232c64a1c", + "typical_tree_max_00.in": "ca2bb3885f080e35899420d5bc8ec0e7b0a5cdef130b4758872bb4b358abbb51", + "typical_tree_max_00.out": "17d7827921c5243e7a8770788dff7991f774bd3b748f51177bff5c77db07ed71", + "typical_tree_max_01.in": "1987ea83990af87a98403cf6aea77bb3e446a5bc247637f0a0a8b0d79db3d3e2", + "typical_tree_max_01.out": "80710479dcbb9091af1d3e293804523b57c29a716e686ca788fee8f21a3d639d", + "typical_tree_max_02.in": "7d105c5ff5e0cf5392995019b090c483a0d999708dff0a40eff02c86ad1c5d16", + "typical_tree_max_02.out": "395e43a78bacdb2c6821812416c9738896093b00982693d78a4e419c62a5c806", + "typical_tree_max_03.in": "b9f2b94482ebe0001a9cf665ab23ea247e4796f8d377b4afcbd50d9a2693f7b0", + "typical_tree_max_03.out": "0499b966a8bac0ae97031d42190830ac1f265dc8591801257e36b0a36a8848d4", + "typical_tree_max_04.in": "a88b59cc8626a943d586e92dad6708491c4cb6bbf5d7b6ade5332bbecd8cfd63", + "typical_tree_max_04.out": "8e962e530ee43580b03f234c764ffafd0b6523ec21df22eae5c248649c039d04", + "typical_tree_max_05.in": "4d9b0565f71f34f634f325926edfa314045f991f6db9b5985bef0dd30c8100b8", + "typical_tree_max_05.out": "4c610d911e696b6059dab301d15c63269d1684baee7d827c2d9be6f76a16aee4", + "typical_tree_max_06.in": "7a0a6ddf483ddd8da87e60885d440bf7cb906e7777e483502f6b08fd036ad80d", + "typical_tree_max_06.out": "0d6d0b98a6ee7bd0b68f4c0924397ed202b480fee9ca29e0e4982ebf5196c763", + "typical_tree_max_07.in": "dea2864fd9ca872ed462b04d997238bd313d8b7bec17150b9cca65354734e4dc", + "typical_tree_max_07.out": "73960c2e5855c91a888ae0182dc61a01ef8aa4e24dbab7e0184101512b0e577e", + "typical_tree_max_08.in": "b3836b3b2ac7cea93e18b7bbe776e50fe9d758e8be80626b08bbf692ea4b9a8b", + "typical_tree_max_08.out": "53626225161187fa26a650c1f5a2e1453c050cb4e165a77fa9269f7a3be92e1f", + "typical_tree_max_09.in": "030edc92a2fba1e566740cd2e34eb84651d13da4c562a9bf292507a5692657f6", + "typical_tree_max_09.out": "4f62c9600672121ca33a1223baef50a711e1eb75788b85525280d0aa6c6fdf39" +} \ No newline at end of file diff --git a/graph/point_set_tree_path_composite_sum/info.toml b/graph/point_set_tree_path_composite_sum/info.toml new file mode 100644 index 000000000..53d5d0307 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/info.toml @@ -0,0 +1,30 @@ +title = 'Point Set Tree Path Composite Sum (Fixed Root)' +timelimit = 10.0 +forum = "https://github.com/yosupo06/library-checker-problems/issues/1136" + +[[tests]] + name = "example.in" + number = 2 +[[tests]] + name = "n_hundreds.cpp" + number = 2 +[[tests]] + name = "tiny.in" + number = 2 +[[tests]] + name = "typical_tree_max.cpp" + number = 10 + +[[solutions]] + name = "naive.cpp" + allow_re = true + +[params] + N_MIN = 1 + N_MAX = 200_000 + Q_MIN = 1 + Q_MAX = 200_000 + A_MIN = 0 + B_MIN = 1 + C_MIN = 0 + MOD = 998_244_353 diff --git a/graph/point_set_tree_path_composite_sum/lib/any-direction-tree-dp.hpp b/graph/point_set_tree_path_composite_sum/lib/any-direction-tree-dp.hpp new file mode 100644 index 000000000..a2c316c90 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/lib/any-direction-tree-dp.hpp @@ -0,0 +1,84 @@ +#pragma once +#include "graph.hpp" +#include +#include +#include +#include + +namespace nachia{ + +template< + class S, + class RakeFunc, + class CompressFunc, + typename std::enable_if_t, void*> = nullptr, + typename std::enable_if_t, void*> = nullptr +> +class AnyDirectionTreeDP{ +private: + std::vector low; + std::vector high; + std::vector XorEdge; + std::vector P; + RakeFunc rake; + CompressFunc compress; + +public: + + // S rake(S a, S b) + // S compress(S a, int edgeIndex, int newRoot) + AnyDirectionTreeDP(const Graph& tree, std::vector node, RakeFunc _rake, CompressFunc _compress) + : rake(std::move(_rake)) + , compress(std::move(_compress)) + { + int n = tree.numVertices(); + auto adj = tree.getEdgeIndexArray(true); + XorEdge.resize(n-1); + for(int i=0; i bfs(n, 0); + int bfsi = 1; + P.assign(n, -1); + for(int v : bfs){ + for(int e : adj[v]){ + int w = v ^ XorEdge[e]; + if(P[v] != e){ P[w] = e; bfs[bfsi++] = w; } + } + } + low = node; + for(int i=n-1; i>=1; i--){ + int w = bfs[i]; + int v = w ^ XorEdge[P[w]]; + low[v] = rake(low[v], compress(low[w], P[w], v)); + } + + high = node; + for(int i=0; i=0; ci--){ + int e = adj[v][ci]; + if(P[v] == e) continue; + int w = v ^ XorEdge[e]; + high[w] = fold; + fold = rake(compress(low[w], e, v), fold); + } + fold = node[v]; + for(int ci=0; ci +#include +#include + +namespace nachia{ + +template +class CsrArray{ +public: + struct ConstListRange{ + using iterator = typename std::vector::const_iterator; + iterator begi, endi; + iterator begin() const { return begi; } + iterator end() const { return endi; } + int size() const { return (int)std::distance(begi, endi); } + const Elem& operator[](int i) const { return begi[i]; } + }; +private: + int m_n; + std::vector m_list; + std::vector m_pos; +public: + CsrArray() : m_n(0), m_list(), m_pos() {} + static CsrArray Construct(int n, std::vector> items){ + CsrArray res; + res.m_n = n; + std::vector buf(n+1, 0); + for(auto& [u,v] : items){ ++buf[u]; } + for(int i=1; i<=n; i++) buf[i] += buf[i-1]; + res.m_list.resize(buf[n]); + for(int i=(int)items.size()-1; i>=0; i--){ + res.m_list[--buf[items[i].first]] = std::move(items[i].second); + } + res.m_pos = std::move(buf); + return res; + } + ConstListRange operator[](int u) const { return ConstListRange{ m_list.begin() + m_pos[u], m_list.begin() + m_pos[u+1] }; } + int size() const { return m_n; } +}; + +} // namespace nachia diff --git a/graph/point_set_tree_path_composite_sum/lib/graph.hpp b/graph/point_set_tree_path_composite_sum/lib/graph.hpp new file mode 100644 index 000000000..f59ad1120 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/lib/graph.hpp @@ -0,0 +1,40 @@ +#pragma once +#include +#include +#include +#include "csr-array.hpp" + +namespace nachia{ + + +struct Graph { +public: + struct Edge{ + int from, to; + }; + using Base = std::vector>; + Graph(int n = 0, bool undirected = false, int m = 0) : m_n(n), m_e(m), m_isUndir(undirected) {} + int numVertices() const noexcept { return m_n; } + int numEdges() const noexcept { return int(m_e.size()); } + int addEdge(int from, int to){ m_e.push_back({ from, to }); return numEdges() - 1; } + Edge& operator[](int ei) noexcept { return m_e[ei]; } + const Edge& operator[](int ei) const noexcept { return m_e[ei]; } + bool isUndirected() const noexcept { return m_isUndir; } + CsrArray getEdgeIndexArray(bool undirected) const { + std::vector> src; + src.reserve(numEdges() * (undirected ? 2 : 1)); + for(int i=0; i::Construct(numVertices(), src); + } + CsrArray getEdgeIndexArray() const { return getEdgeIndexArray(isUndirected()); } +private: + int m_n; + std::vector m_e; + bool m_isUndir; +}; + +} // namespace nachia diff --git a/graph/point_set_tree_path_composite_sum/lib/static-modint.hpp b/graph/point_set_tree_path_composite_sum/lib/static-modint.hpp new file mode 100644 index 000000000..4a8256a7f --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/lib/static-modint.hpp @@ -0,0 +1,42 @@ +#pragma once +#include +#include + +namespace nachia{ + +template +struct StaticModint{ +private: + using u64 = unsigned long long; + unsigned int x; +public: + + using my_type = StaticModint; + template< class Elem > + static Elem safe_mod(Elem x){ + if(x < 0){ + if(0 <= x+MOD) return x + MOD; + return MOD - ((-(x+MOD)-1) % MOD + 1); + } + return x % MOD; + } + + StaticModint() : x(0){} + StaticModint(const my_type& a) : x(a.x){} + StaticModint& operator=(const my_type&) = default; + template< class Elem > + StaticModint(Elem v) : x(safe_mod(v)){} + unsigned int operator*() const noexcept { return x; } + my_type& operator+=(const my_type& r) noexcept { auto t = x + r.x; if(t >= MOD) t -= MOD; x = t; return *this; } + my_type operator+(const my_type& r) const noexcept { my_type res = *this; return res += r; } + my_type& operator-=(const my_type& r) noexcept { auto t = x + MOD - r.x; if(t >= MOD) t -= MOD; x = t; return *this; } + my_type operator-(const my_type& r) const noexcept { my_type res = *this; return res -= r; } + my_type operator-() const noexcept { my_type res = *this; res.x = ((res.x == 0) ? 0 : (MOD - res.x)); return res; } + my_type& operator*=(const my_type& r)noexcept { x = (u64)x * r.x % MOD; return *this; } + my_type operator*(const my_type& r) const noexcept { my_type res = *this; return res *= r; } + unsigned int val() const noexcept { return x; } + static constexpr unsigned int mod() { return MOD; } + static my_type raw(unsigned int val) noexcept { auto res = my_type(); res.x = val; return res; } +}; + +} diff --git a/graph/point_set_tree_path_composite_sum/sol/correct.cpp b/graph/point_set_tree_path_composite_sum/sol/correct.cpp new file mode 100644 index 000000000..9278cdbad --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/sol/correct.cpp @@ -0,0 +1,887 @@ +/** + * date : 2024-04-29 15:27:21 + * author : Nyaan + */ + +#define NDEBUG + + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +using namespace std; + + + + + +using namespace std; + + + + +using namespace std; + +namespace internal { +template +using is_broadly_integral = + typename conditional_t || is_same_v || + is_same_v, + true_type, false_type>::type; + +template +using is_broadly_signed = + typename conditional_t || is_same_v, + true_type, false_type>::type; + +template +using is_broadly_unsigned = + typename conditional_t || is_same_v, + true_type, false_type>::type; + +#define ENABLE_VALUE(x) \ + template \ + constexpr bool x##_v = x::value; + +ENABLE_VALUE(is_broadly_integral); +ENABLE_VALUE(is_broadly_signed); +ENABLE_VALUE(is_broadly_unsigned); +#undef ENABLE_VALUE + +#define ENABLE_HAS_TYPE(var) \ + template \ + struct has_##var : false_type {}; \ + template \ + struct has_##var> : true_type {}; \ + template \ + constexpr auto has_##var##_v = has_##var::value; + +#define ENABLE_HAS_VAR(var) \ + template \ + struct has_##var : false_type {}; \ + template \ + struct has_##var> : true_type {}; \ + template \ + constexpr auto has_##var##_v = has_##var::value; + +} // namespace internal + + +namespace fastio { +static constexpr int SZ = 1 << 17; +static constexpr int offset = 64; +char inbuf[SZ], outbuf[SZ]; +int in_left = 0, in_right = 0, out_right = 0; + +struct Pre { + char num[40000]; + constexpr Pre() : num() { + for (int i = 0; i < 10000; i++) { + int n = i; + for (int j = 3; j >= 0; j--) { + num[i * 4 + j] = n % 10 + '0'; + n /= 10; + } + } + } +} constexpr pre; + +void load() { + int len = in_right - in_left; + memmove(inbuf, inbuf + in_left, len); + in_right = len + fread(inbuf + len, 1, SZ - len, stdin); + in_left = 0; +} +void flush() { + fwrite(outbuf, 1, out_right, stdout); + out_right = 0; +} +void skip_space() { + if (in_left + offset > in_right) load(); + while (inbuf[in_left] <= ' ') in_left++; +} + +void single_read(char& c) { + if (in_left + offset > in_right) load(); + skip_space(); + c = inbuf[in_left++]; +} +void single_read(string& S) { + skip_space(); + while (true) { + if (in_left == in_right) load(); + int i = in_left; + for (; i != in_right; i++) { + if (inbuf[i] <= ' ') break; + } + copy(inbuf + in_left, inbuf + i, back_inserter(S)); + in_left = i; + if (i != in_right) break; + } +} +template >* = nullptr> +void single_read(T& x) { + if (in_left + offset > in_right) load(); + skip_space(); + char c = inbuf[in_left++]; + [[maybe_unused]] bool minus = false; + if constexpr (internal::is_broadly_signed_v) { + if (c == '-') minus = true, c = inbuf[in_left++]; + } + x = 0; + while (c >= '0') { + x = x * 10 + (c & 15); + c = inbuf[in_left++]; + } + if constexpr (internal::is_broadly_signed_v) { + if (minus) x = -x; + } +} +void rd() {} +template +void rd(Head& head, Tail&... tail) { + single_read(head); + rd(tail...); +} + +void single_write(const char& c) { + if (out_right > SZ - offset) flush(); + outbuf[out_right++] = c; +} +void single_write(const bool& b) { + if (out_right > SZ - offset) flush(); + outbuf[out_right++] = b ? '1' : '0'; +} +void single_write(const string& S) { + flush(), fwrite(S.data(), 1, S.size(), stdout); +} +void single_write(const char* p) { flush(), fwrite(p, 1, strlen(p), stdout); } +template >* = nullptr> +void single_write(const T& _x) { + if (out_right > SZ - offset) flush(); + if (_x == 0) { + outbuf[out_right++] = '0'; + return; + } + T x = _x; + if constexpr (internal::is_broadly_signed_v) { + if (x < 0) outbuf[out_right++] = '-', x = -x; + } + constexpr int buffer_size = sizeof(T) * 10 / 4; + char buf[buffer_size]; + int i = buffer_size; + while (x >= 10000) { + i -= 4; + memcpy(buf + i, pre.num + (x % 10000) * 4, 4); + x /= 10000; + } + if (x < 100) { + if (x < 10) { + outbuf[out_right] = '0' + x; + ++out_right; + } else { + uint32_t q = (uint32_t(x) * 205) >> 11; + uint32_t r = uint32_t(x) - q * 10; + outbuf[out_right] = '0' + q; + outbuf[out_right + 1] = '0' + r; + out_right += 2; + } + } else { + if (x < 1000) { + memcpy(outbuf + out_right, pre.num + (x << 2) + 1, 3); + out_right += 3; + } else { + memcpy(outbuf + out_right, pre.num + (x << 2), 4); + out_right += 4; + } + } + memcpy(outbuf + out_right, buf + i, buffer_size - i); + out_right += buffer_size - i; +} +void wt() {} +template +void wt(const Head& head, const Tail&... tail) { + single_write(head); + wt(std::forward(tail)...); +} +template +void wtn(const Args&... x) { + wt(std::forward(x)...); + wt('\n'); +} + +struct Dummy { + Dummy() { atexit(flush); } +} dummy; + +} // namespace fastio +using fastio::rd; +using fastio::skip_space; +using fastio::wt; +using fastio::wtn; + + +// + + +template +struct LazyMontgomeryModInt { + using mint = LazyMontgomeryModInt; + using i32 = int32_t; + using u32 = uint32_t; + using u64 = uint64_t; + + static constexpr u32 get_r() { + u32 ret = mod; + for (i32 i = 0; i < 4; ++i) ret *= 2 - mod * ret; + return ret; + } + + static constexpr u32 r = get_r(); + static constexpr u32 n2 = -u64(mod) % mod; + static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30"); + static_assert((mod & 1) == 1, "invalid, mod % 2 == 0"); + static_assert(r * mod == 1, "this code has bugs."); + + u32 a; + + constexpr LazyMontgomeryModInt() : a(0) {} + constexpr LazyMontgomeryModInt(const int64_t &b) + : a(reduce(u64(b % mod + mod) * n2)){}; + + static constexpr u32 reduce(const u64 &b) { + return (b + u64(u32(b) * u32(-r)) * mod) >> 32; + } + + constexpr mint &operator+=(const mint &b) { + if (i32(a += b.a - 2 * mod) < 0) a += 2 * mod; + return *this; + } + + constexpr mint &operator-=(const mint &b) { + if (i32(a -= b.a) < 0) a += 2 * mod; + return *this; + } + + constexpr mint &operator*=(const mint &b) { + a = reduce(u64(a) * b.a); + return *this; + } + + constexpr mint &operator/=(const mint &b) { + *this *= b.inverse(); + return *this; + } + + constexpr mint operator+(const mint &b) const { return mint(*this) += b; } + constexpr mint operator-(const mint &b) const { return mint(*this) -= b; } + constexpr mint operator*(const mint &b) const { return mint(*this) *= b; } + constexpr mint operator/(const mint &b) const { return mint(*this) /= b; } + constexpr bool operator==(const mint &b) const { + return (a >= mod ? a - mod : a) == (b.a >= mod ? b.a - mod : b.a); + } + constexpr bool operator!=(const mint &b) const { + return (a >= mod ? a - mod : a) != (b.a >= mod ? b.a - mod : b.a); + } + constexpr mint operator-() const { return mint() - mint(*this); } + constexpr mint operator+() const { return mint(*this); } + + constexpr mint pow(u64 n) const { + mint ret(1), mul(*this); + while (n > 0) { + if (n & 1) ret *= mul; + mul *= mul; + n >>= 1; + } + return ret; + } + + constexpr mint inverse() const { + int x = get(), y = mod, u = 1, v = 0, t = 0, tmp = 0; + while (y > 0) { + t = x / y; + x -= t * y, u -= t * v; + tmp = x, x = y, y = tmp; + tmp = u, u = v, v = tmp; + } + return mint{u}; + } + + friend ostream &operator<<(ostream &os, const mint &b) { + return os << b.get(); + } + + friend istream &operator>>(istream &is, mint &b) { + int64_t t; + is >> t; + b = LazyMontgomeryModInt(t); + return (is); + } + + constexpr u32 get() const { + u32 ret = reduce(a); + return ret >= mod ? ret - mod : ret; + } + + static constexpr u32 get_mod() { return mod; } +}; + + +// + + +namespace DynamicRerootingImpl { +template +struct SplayTreeforDashedEdge { + struct Node { + Node *l, *r, *p; + Point key, sum; + + explicit Node(const Point &_key) + : l(nullptr), r(nullptr), p(nullptr), key(_key), sum(_key) {} + }; + + SplayTreeforDashedEdge() {} + + using NP = Node *; + + void rotr(NP t) { + NP x = t->p, y = x->p; + if ((x->l = t->r)) t->r->p = x; + t->r = x, x->p = t; + update(x), update(t); + if ((t->p = y)) { + if (y->l == x) y->l = t; + if (y->r == x) y->r = t; + } + } + + void rotl(NP t) { + NP x = t->p, y = x->p; + if ((x->r = t->l)) t->l->p = x; + t->l = x, x->p = t; + update(x), update(t); + if ((t->p = y)) { + if (y->l == x) y->l = t; + if (y->r == x) y->r = t; + } + } + + void update(NP t) { + t->sum = t->key; + if (t->l) t->sum = rake(t->sum, t->l->sum); + if (t->r) t->sum = rake(t->sum, t->r->sum); + } + + NP get_right(NP t) { + while (t->r) t = t->r; + return t; + } + + NP alloc(const Point &v) { + auto t = new Node(v); + update(t); + return t; + } + + void splay(NP t) { + while (t->p) { + NP q = t->p; + if (!q->p) { + if (q->l == t) + rotr(t); + else + rotl(t); + } else { + NP r = q->p; + if (r->l == q) { + if (q->l == t) + rotr(q), rotr(t); + else + rotl(t), rotr(t); + } else { + if (q->r == t) + rotl(q), rotl(t); + else + rotr(t), rotl(t); + } + } + } + } + + NP insert(NP t, const Point &v) { + if (not t) { + t = alloc(v); + return t; + } else { + NP cur = get_right(t), z = alloc(v); + splay(cur); + z->p = cur; + cur->r = z; + update(cur); + splay(z); + return z; + } + } + + NP erase(NP t) { + splay(t); + NP x = t->l, y = t->r; + delete t; + if (not x) { + t = y; + if (t) t->p = nullptr; + } else if (not y) { + t = x; + t->p = nullptr; + } else { + x->p = nullptr; + t = get_right(x); + splay(t); + t->r = y; + y->p = t; + update(t); + } + return t; + } +}; + +template +struct TopTree { + private: + struct Node { + Node *l, *r, *p; + Info info; + Path key, sum, mus; + typename SplayTreeforDashedEdge::Node *light, *belong; + bool rev; + + bool is_root() const { return not p or (p->l != this and p->r != this); } + + explicit Node(const Info _info) + : l(nullptr), + r(nullptr), + p(nullptr), + info(_info), + light(nullptr), + belong(nullptr), + rev(false) {} + }; + + public: + using NP = Node *; + SplayTreeforDashedEdge splay_tree; + + private: + void toggle(NP t) { + swap(t->l, t->r); + swap(t->sum, t->mus); + t->rev ^= true; + } + + void rotr(NP t) { + NP x = t->p, y = x->p; + push(x), push(t); + if ((x->l = t->r)) t->r->p = x; + t->r = x, x->p = t; + update(x), update(t); + if ((t->p = y)) { + if (y->l == x) y->l = t; + if (y->r == x) y->r = t; + } + } + + void rotl(NP t) { + NP x = t->p, y = x->p; + push(x), push(t); + if ((x->r = t->l)) t->l->p = x; + t->l = x, x->p = t; + update(x), update(t); + if ((t->p = y)) { + if (y->l == x) y->l = t; + if (y->r == x) y->r = t; + } + } + + public: + TopTree() : splay_tree{} {} + + void push(NP t) { + if (t->rev) { + if (t->l) toggle(t->l); + if (t->r) toggle(t->r); + t->rev = false; + } + } + + void push_rev(NP t) { + if (t->rev) { + if (t->l) toggle(t->l); + if (t->r) toggle(t->r); + t->rev = false; + } + } + + void update(NP t) { + Path key = t->light ? add_vertex(t->light->sum, t->info) : vertex(t->info); + Path sum = key, mus = key; + if (t->l) sum = compress(t->l->sum, sum), mus = compress(mus, t->l->mus); + if (t->r) sum = compress(sum, t->r->sum), mus = compress(t->r->mus, mus); + t->key = key, t->sum = sum, t->mus = mus; + } + + void splay(NP t) { + push(t); + { + NP rot = t; + while (not rot->is_root()) rot = rot->p; + t->belong = rot->belong; + if (t != rot) rot->belong = nullptr; + } + while (not t->is_root()) { + NP q = t->p; + if (q->is_root()) { + push_rev(q), push_rev(t); + if (q->l == t) + rotr(t); + else + rotl(t); + } else { + NP r = q->p; + push_rev(r), push_rev(q), push_rev(t); + if (r->l == q) { + if (q->l == t) + rotr(q), rotr(t); + else + rotl(t), rotr(t); + } else { + if (q->r == t) + rotl(q), rotl(t); + else + rotr(t), rotl(t); + } + } + } + } + + NP expose(NP t) { + NP rp = nullptr; + for (NP cur = t; cur; cur = cur->p) { + splay(cur); + if (cur->r) { + cur->light = splay_tree.insert(cur->light, add_edge(cur->r->sum)); + cur->r->belong = cur->light; + } + cur->r = rp; + if (cur->r) { + splay_tree.splay(cur->r->belong); + push(cur->r); + cur->light = splay_tree.erase(cur->r->belong); + } + update(cur); + rp = cur; + } + splay(t); + return rp; + } + + void link(NP child, NP parent) { + expose(parent); + expose(child); + child->p = parent; + parent->r = child; + update(parent); + } + + void cut(NP child) { + expose(child); + NP parent = child->l; + child->l = nullptr; + parent->p = nullptr; + update(child); + } + + void evert(NP t) { + expose(t); + toggle(t); + push(t); + } + + NP alloc(const Info &info) { + NP t = new Node(info); + update(t); + return t; + } + + bool is_connected(NP u, NP v) { + expose(u), expose(v); + return u == v or u->p; + } + + NP lca(NP u, NP v) { + if (not is_connected(u, v)) return nullptr; + expose(u); + return expose(v); + } + + void set_key(NP t, const Info &v) { + expose(t); + t->info = v; + update(t); + } + + // u を根とする sum + Path query(NP u) { + evert(u); + return u->sum; + } + + // root を根, u を部分木の根とする sum + Path query_subtree(NP root, NP u) { + evert(root); + expose(u); + NP l = u->l; + u->l = nullptr; + update(u); + auto ret = u->sum; + u->l = l; + update(u); + return ret; + } +}; + +template +struct DynamicRerooting { + int n; + TopTree tt; + using NP = typename decltype(tt)::NP; + vector vs; + + DynamicRerooting(int _n, const vector &info) : n(_n), vs(n) { + for (int i = 0; i < n; i++) vs[i] = tt.alloc(info[i]); + } + // u-v 間に辺を追加 + void add_edge(int u, int v) { + tt.evert(vs[u]); + tt.link(vs[u], vs[v]); + } + // u-v 間の辺を削除 + void del_edge(int u, int v) { + tt.evert(vs[u]); + tt.cut(vs[v]); + } + // 頂点 u の情報を取得 + Info get_info(int u) { return vs[u]->info; } + // 頂点 u の情報を設定 + void set_info(int u, const Info &info) { tt.set_key(vs[u], info); } + // 頂点 u を根とするクエリ + Path query(int u) { return tt.query(vs[u]); } + // 頂点 root を根, 頂点 u を部分木の根とするクエリ + Path query_subtree(int root, int u) { + return tt.query_subtree(vs[root], vs[u]); + } +}; + +} // namespace DynamicRerootingImpl + +using DynamicRerootingImpl::DynamicRerooting; +using DynamicRerootingImpl::TopTree; + +/* +struct Path { + +}; +struct Point { + +}; +struct Info { + +}; +Path vertex(const Info &i) { + +} +Path compress(const Path &p, const Path &c) { + +} +Point rake(const Point &a, const Point &b) { + +} +Point add_edge(const Path &a) { + +} +Path add_vertex(const Point &a, const Info &i) { + +} + +using DR = DynamicRerooting; +*/ + + +// +using mint = LazyMontgomeryModInt<998244353>; + +struct Path { + mint a, b, s, x; +}; +struct Point { + mint s, x; +}; +struct Info { + bool vertex; + mint x, y; +}; +Path vertex(const Info &i) { + if (i.vertex) return {1, 0, i.x, 1}; + return {i.x, i.y, 0, 0}; +} +Path compress(const Path &p, const Path &c) { + return {p.a * c.a, p.a * c.b + p.b, p.s + p.a * c.s + p.b * c.x, p.x + c.x}; +} +Point rake(const Point &a, const Point &b) { return {a.s + b.s, a.x + b.x}; } +Point add_edge(const Path &a) { return {a.s, a.x}; } +Path add_vertex(const Point &a, const Info &i) { + if (i.vertex) return {1, 0, a.s + i.x, a.x + 1}; + return {i.x, i.y, a.s * i.x + a.x * i.y, a.x}; +} + +using DR = DynamicRerooting; + +int main() { + int N, Q; + rd(N, Q); + vector A(N); + for (auto &x : A) rd(x); + vector U(N - 1), V(N - 1), B(N - 1), C(N - 1); + for (int i = 0; i < N - 1; i++) rd(U[i], V[i], B[i], C[i]); + + vector info(2 * N - 1); + for (int i = 0; i < N; i++) info[i] = {true, A[i], 0}; + for (int i = 0; i < N - 1; i++) info[N + i] = {false, B[i], C[i]}; + + DR dr{2 * N - 1, info}; + for (int i = 0; i < N - 1; i++) { + dr.add_edge(N + i, U[i]), dr.add_edge(N + i, V[i]); + } + + while (Q--) { + int cmd, i, x; + rd(cmd, i, x); + if (cmd == 0) { + dr.set_info(i, {true, x, 0}); + } else { + int y; + rd(y); + dr.set_info(N + i, {false, x, y}); + } + int r; + rd(r); + wtn(dr.query(r).s.get()); + } +} diff --git a/graph/point_set_tree_path_composite_sum/sol/naive.cpp b/graph/point_set_tree_path_composite_sum/sol/naive.cpp new file mode 100644 index 000000000..3b45cf3ca --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/sol/naive.cpp @@ -0,0 +1,411 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace internal { +template +using is_broadly_integral = + typename conditional_t || is_same_v || + is_same_v, + true_type, false_type>::type; + +template +using is_broadly_signed = + typename conditional_t || is_same_v, + true_type, false_type>::type; + +template +using is_broadly_unsigned = + typename conditional_t || is_same_v, + true_type, false_type>::type; + +#define ENABLE_VALUE(x) \ + template \ + constexpr bool x##_v = x::value; + +ENABLE_VALUE(is_broadly_integral); +ENABLE_VALUE(is_broadly_signed); +ENABLE_VALUE(is_broadly_unsigned); +#undef ENABLE_VALUE + +#define ENABLE_HAS_TYPE(var) \ + template \ + struct has_##var : false_type {}; \ + template \ + struct has_##var> : true_type {}; \ + template \ + constexpr auto has_##var##_v = has_##var::value; + +#define ENABLE_HAS_VAR(var) \ + template \ + struct has_##var : false_type {}; \ + template \ + struct has_##var> : true_type {}; \ + template \ + constexpr auto has_##var##_v = has_##var::value; + +} // namespace internal + +namespace fastio { +static constexpr int SZ = 1 << 17; +static constexpr int offset = 64; +char inbuf[SZ], outbuf[SZ]; +int in_left = 0, in_right = 0, out_right = 0; + +struct Pre { + char num[40000]; + constexpr Pre() : num() { + for (int i = 0; i < 10000; i++) { + int n = i; + for (int j = 3; j >= 0; j--) { + num[i * 4 + j] = n % 10 + '0'; + n /= 10; + } + } + } +} constexpr pre; + +void load() { + int len = in_right - in_left; + memmove(inbuf, inbuf + in_left, len); + in_right = len + fread(inbuf + len, 1, SZ - len, stdin); + in_left = 0; +} +void flush() { + fwrite(outbuf, 1, out_right, stdout); + out_right = 0; +} +void skip_space() { + if (in_left + offset > in_right) load(); + while (inbuf[in_left] <= ' ') in_left++; +} + +void single_read(char& c) { + if (in_left + offset > in_right) load(); + skip_space(); + c = inbuf[in_left++]; +} +void single_read(string& S) { + skip_space(); + while (true) { + if (in_left == in_right) load(); + int i = in_left; + for (; i != in_right; i++) { + if (inbuf[i] <= ' ') break; + } + copy(inbuf + in_left, inbuf + i, back_inserter(S)); + in_left = i; + if (i != in_right) break; + } +} +template >* = nullptr> +void single_read(T& x) { + if (in_left + offset > in_right) load(); + skip_space(); + char c = inbuf[in_left++]; + [[maybe_unused]] bool minus = false; + if constexpr (internal::is_broadly_signed_v) { + if (c == '-') minus = true, c = inbuf[in_left++]; + } + x = 0; + while (c >= '0') { + x = x * 10 + (c & 15); + c = inbuf[in_left++]; + } + if constexpr (internal::is_broadly_signed_v) { + if (minus) x = -x; + } +} +void rd() {} +template +void rd(Head& head, Tail&... tail) { + single_read(head); + rd(tail...); +} + +void single_write(const char& c) { + if (out_right > SZ - offset) flush(); + outbuf[out_right++] = c; +} +void single_write(const bool& b) { + if (out_right > SZ - offset) flush(); + outbuf[out_right++] = b ? '1' : '0'; +} +void single_write(const string& S) { + flush(), fwrite(S.data(), 1, S.size(), stdout); +} +void single_write(const char* p) { flush(), fwrite(p, 1, strlen(p), stdout); } +template >* = nullptr> +void single_write(const T& _x) { + if (out_right > SZ - offset) flush(); + if (_x == 0) { + outbuf[out_right++] = '0'; + return; + } + T x = _x; + if constexpr (internal::is_broadly_signed_v) { + if (x < 0) outbuf[out_right++] = '-', x = -x; + } + constexpr int buffer_size = sizeof(T) * 10 / 4; + char buf[buffer_size]; + int i = buffer_size; + while (x >= 10000) { + i -= 4; + memcpy(buf + i, pre.num + (x % 10000) * 4, 4); + x /= 10000; + } + if (x < 100) { + if (x < 10) { + outbuf[out_right] = '0' + x; + ++out_right; + } else { + uint32_t q = (uint32_t(x) * 205) >> 11; + uint32_t r = uint32_t(x) - q * 10; + outbuf[out_right] = '0' + q; + outbuf[out_right + 1] = '0' + r; + out_right += 2; + } + } else { + if (x < 1000) { + memcpy(outbuf + out_right, pre.num + (x << 2) + 1, 3); + out_right += 3; + } else { + memcpy(outbuf + out_right, pre.num + (x << 2), 4); + out_right += 4; + } + } + memcpy(outbuf + out_right, buf + i, buffer_size - i); + out_right += buffer_size - i; +} +void wt() {} +template +void wt(const Head& head, const Tail&... tail) { + single_write(head); + wt(std::forward(tail)...); +} +template +void wtn(const Args&... x) { + wt(std::forward(x)...); + wt('\n'); +} + +struct Dummy { + Dummy() { atexit(flush); } +} dummy; + +} // namespace fastio +using fastio::rd; +using fastio::skip_space; +using fastio::wt; +using fastio::wtn; + +// + +template +struct LazyMontgomeryModInt { + using mint = LazyMontgomeryModInt; + using i32 = int32_t; + using u32 = uint32_t; + using u64 = uint64_t; + + static constexpr u32 get_r() { + u32 ret = mod; + for (i32 i = 0; i < 4; ++i) ret *= 2 - mod * ret; + return ret; + } + + static constexpr u32 r = get_r(); + static constexpr u32 n2 = -u64(mod) % mod; + static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30"); + static_assert((mod & 1) == 1, "invalid, mod % 2 == 0"); + static_assert(r * mod == 1, "this code has bugs."); + + u32 a; + + constexpr LazyMontgomeryModInt() : a(0) {} + constexpr LazyMontgomeryModInt(const int64_t& b) + : a(reduce(u64(b % mod + mod) * n2)){}; + + static constexpr u32 reduce(const u64& b) { + return (b + u64(u32(b) * u32(-r)) * mod) >> 32; + } + + constexpr mint& operator+=(const mint& b) { + if (i32(a += b.a - 2 * mod) < 0) a += 2 * mod; + return *this; + } + + constexpr mint& operator-=(const mint& b) { + if (i32(a -= b.a) < 0) a += 2 * mod; + return *this; + } + + constexpr mint& operator*=(const mint& b) { + a = reduce(u64(a) * b.a); + return *this; + } + + constexpr mint& operator/=(const mint& b) { + *this *= b.inverse(); + return *this; + } + + constexpr mint operator+(const mint& b) const { return mint(*this) += b; } + constexpr mint operator-(const mint& b) const { return mint(*this) -= b; } + constexpr mint operator*(const mint& b) const { return mint(*this) *= b; } + constexpr mint operator/(const mint& b) const { return mint(*this) /= b; } + constexpr bool operator==(const mint& b) const { + return (a >= mod ? a - mod : a) == (b.a >= mod ? b.a - mod : b.a); + } + constexpr bool operator!=(const mint& b) const { + return (a >= mod ? a - mod : a) != (b.a >= mod ? b.a - mod : b.a); + } + constexpr mint operator-() const { return mint() - mint(*this); } + constexpr mint operator+() const { return mint(*this); } + + constexpr mint pow(u64 n) const { + mint ret(1), mul(*this); + while (n > 0) { + if (n & 1) ret *= mul; + mul *= mul; + n >>= 1; + } + return ret; + } + + constexpr mint inverse() const { + int x = get(), y = mod, u = 1, v = 0, t = 0, tmp = 0; + while (y > 0) { + t = x / y; + x -= t * y, u -= t * v; + tmp = x, x = y, y = tmp; + tmp = u, u = v, v = tmp; + } + return mint{u}; + } + + friend ostream& operator<<(ostream& os, const mint& b) { + return os << b.get(); + } + + friend istream& operator>>(istream& is, mint& b) { + int64_t t; + is >> t; + b = LazyMontgomeryModInt(t); + return (is); + } + + constexpr u32 get() const { + u32 ret = reduce(a); + return ret >= mod ? ret - mod : ret; + } + + static constexpr u32 get_mod() { return mod; } +}; + +// +using mint = LazyMontgomeryModInt<998244353>; + +#define rep(i, n) for (int i = 0; i < (n); i++) +#define each(x, v) for (auto&& x : v) + +vector naive(int N, int Q, vector A, vector U, vector V, + vector B, vector C, vector cmd, vector I, + vector X, vector Y, vector R) { + vector ans(Q); + + rep(q, Q) { + if (cmd[q] == 0) { + A[I[q]] = X[q]; + } else { + B[I[q]] = X[q], C[I[q]] = Y[q]; + } + vector>>> g(N); + rep(i, N - 1) { + g[U[i]].emplace_back(V[i], make_pair(B[i], C[i])); + g[V[i]].emplace_back(U[i], make_pair(B[i], C[i])); + } + auto dfs = [&](auto rc, int c, int p) -> pair { + pair res{0, 0}; + for (auto& dab : g[c]) { + int d = dab.first; + if (d == p) continue; + auto [a, b] = dab.second; + auto [x, n] = rc(rc, d, c); + res.first += (x + A[d]) * a + b * (n + 1); + res.second += n + 1; + } + return res; + }; + ans[q] = dfs(dfs, R[q], -1).first + A[R[q]]; + } + return ans; +} + +int main() { + int N, Q; + rd(N, Q); + vector A(N); + for (auto& x : A) rd(x); + vector U(N - 1), V(N - 1), B(N - 1), C(N - 1); + rep(i, N - 1) rd(U[i], V[i], B[i], C[i]); + vector cmd(Q), I(Q), X(Q), Y(Q, -1), R(Q); + rep(i, Q) { + rd(cmd[i], I[i], X[i]); + if (cmd[i] == 1) rd(Y[i]); + rd(R[i]); + } + + if(N * Q > 1e8) exit(1); + auto an = naive(N, Q, A, U, V, B, C, cmd, I, X, Y, R); + for (auto& x : an) wtn(x.get()); +} diff --git a/graph/point_set_tree_path_composite_sum/task.md b/graph/point_set_tree_path_composite_sum/task.md new file mode 100644 index 000000000..87e84895e --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/task.md @@ -0,0 +1,124 @@ +## @{keyword.statement} + +@{lang.en} + +You are given + +- a tree with $N$ vertices, +- $N$ integers $a _ 0, a _ 1, \ldots, a _ {N-1}$ , +- $N-1$ integers $b _ 0, b _ 1, \ldots, b _ {N-2}$ , and +- $N-1$ integers $c _ 0, c _ 1, \ldots, c _ {N-2}$ . + +Edge $i$ connects vertices $u _ i$ and $v _ i$ bidirectionally. + +For an integer $e$ that satisfies $0 \leq e \leq N-2$ , let $f _ e(x) = b _ e x + c _ e$ . + +Let $e _ 0, e _ 1, \ldots, e _ k$ be the edges on the simple path from vertex $x$ to vertex $y$ in order, and define $P(x, y) = f _ {e _ 0}(f _ {e _ 1}(\ldots f _ {e _ k}(a _ y) \ldots ))$ . + +Process $Q$ queries in the order they are given. There are two types of queries: + +- `0 w x r` : Update $a_w$ to $x$ and then print $\left(\sum_{v=0}^{N-1} P(r, v)\right) \bmod @{param.MOD}$. +- `1 e y z r` : Update $(b_e, c_e)$ to $(y, z)$ and then print $\left(\sum_{v=0}^{N-1} P(r, v)\right) \bmod @{param.MOD}$. + + +@{lang.ja} + +- $N$ 頂点の木、 +- $N$ 個の整数 $a _ 0 , a _ 1 , \ldots , a _ {N-1}$ 、 +- $N-1$ 個の整数 $b _ 0 , b _ 1 , \ldots , b _ {N-2}$ 、 +- $N-1$ 個の整数 $c _ 0 , c _ 1 , \ldots , c _ {N-2}$ + +が与えられます。辺 $i$ は頂点 $u _ i$ と頂点 $v _ i$ を双方向に結びます。 + +$0\leq e \leq N-2$ を満たす整数 $e$ について、 $f _ e (x) = b _ e x + c _ e$ とします。 + +頂点 $x$ から頂点 $y$ までの単純パスに含まれる辺を順に $e _ 0,e _ 1, \ldots , e _ k$ として、 $P(x, y) = f _ {e _ 0}(f _ {e _ 1}(\ldots f _ {e _ k}(a _ y) \ldots ))$ とします。 + +$Q$ 個のクエリを与えられる順に処理してください。各クエリは次の $2$ 種類のいずれかです。 + +- `0 w x r` : $a_w$ を $x$ に更新して、その後 $\left(\sum_{v=0}^{N-1} P(r, v)\right) \bmod @{param.MOD}$ を出力する。 +- `1 e y z r` : $(b_e, c_e)$ を $(y, z)$ に更新して、その後 $\left(\sum_{v=0}^{N-1} P(r, v)\right) \bmod @{param.MOD}$ を出力する。 + +@{lang.end} + +## @{keyword.constraints} + +@{lang.en} +- All input are integers. +- $@{param.N_MIN} \leq N \leq @{param.N_MAX}$ +- $0 \leq u _ i, v _ i \leq N - 1$ +- $@{param.A_MIN} \leq a _ w \lt @{param.MOD}$ +- $@{param.B_MIN} \leq b _ e \lt @{param.MOD}$ +- $@{param.C_MIN} \leq c _ e \lt @{param.MOD}$ +- $0 \leq w \leq N - 1$ +- $0 \leq e \leq N - 2$ +- $@{param.A_MIN} \leq x \lt @{param.MOD}$ +- $@{param.B_MIN} \leq y \lt @{param.MOD}$ +- $@{param.C_MIN} \leq z \lt @{param.MOD}$ +- $0 \leq r \leq N - 1$ + +@{lang.ja} +- 入力は全て整数である +- $@{param.N_MIN} \leq N \leq @{param.N_MAX}$ +- $0 \leq u _ i, v _ i \leq N - 1$ +- $@{param.A_MIN} \leq a _ w \lt @{param.MOD}$ +- $@{param.B_MIN} \leq b _ e \lt @{param.MOD}$ +- $@{param.C_MIN} \leq c _ e \lt @{param.MOD}$ +- $0 \leq w \leq N - 1$ +- $0 \leq e \leq N - 2$ +- $@{param.A_MIN} \leq x \lt @{param.MOD}$ +- $@{param.B_MIN} \leq y \lt @{param.MOD}$ +- $@{param.C_MIN} \leq z \lt @{param.MOD}$ +- $0 \leq r \leq N - 1$ + +@{lang.end} + +## @{keyword.input} + +@{lang.en} + +$\mathrm{Query}_i$ represents the $i$-th query. + +@{lang.ja} + +$\mathrm{Query}_i$ は $i$ 番目のクエリを意味する。 + +@{lang.end} + +``` +$N$ $Q$ +$a _ 0$ $a _ 1$ $\ldots$ $a _ {N-1}$ +$u _ 0$ $v _ 0$ $b _ 0$ $c _ 0$ +$u _ 1$ $v _ 1$ $b _ 1$ $c _ 1$ +$\vdots$ +$u _ {N-2}$ $v _ {N-2}$ $b _ {N-2}$ $c _ {N-2}$ +$\mathrm{Query}_0$ +$\mathrm{Query}_1$ +$\vdots$ +$\mathrm{Query}_{Q-1}$ +``` + +## @{keyword.output} + +@{lang.en} + +$p_i$ represents the answer to the $i$-th query. + +@{lang.ja} + +$p_i$ は $i$ 番目のクエリへの答えを意味する。 + +@{lang.end} + +``` +$p_0$ +$p_1$ +$\vdots$ +$p_{Q-1}$ +``` + +## @{keyword.sample} + +@{example.example_00} + +@{example.example_01} diff --git a/graph/point_set_tree_path_composite_sum/verifier.cpp b/graph/point_set_tree_path_composite_sum/verifier.cpp new file mode 100644 index 000000000..9d7126bf8 --- /dev/null +++ b/graph/point_set_tree_path_composite_sum/verifier.cpp @@ -0,0 +1,142 @@ +#include + +#include "params.h" +#include "testlib.h" + +// begin + +#include +#include +#include + +namespace atcoder { + +// Implement (union by size) + (path compression) +// Reference: +// Zvi Galil and Giuseppe F. Italiano, +// Data structures and algorithms for disjoint set union problems +struct dsu { + public: + dsu() : _n(0) {} + explicit dsu(int n) : _n(n), parent_or_size(n, -1) {} + + int merge(int a, int b) { + assert(0 <= a && a < _n); + assert(0 <= b && b < _n); + int x = leader(a), y = leader(b); + if (x == y) return x; + if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y); + parent_or_size[x] += parent_or_size[y]; + parent_or_size[y] = x; + return x; + } + + bool same(int a, int b) { + assert(0 <= a && a < _n); + assert(0 <= b && b < _n); + return leader(a) == leader(b); + } + + int leader(int a) { + assert(0 <= a && a < _n); + if (parent_or_size[a] < 0) return a; + return parent_or_size[a] = leader(parent_or_size[a]); + } + + int size(int a) { + assert(0 <= a && a < _n); + return -parent_or_size[leader(a)]; + } + + std::vector> groups() { + std::vector leader_buf(_n), group_size(_n); + for (int i = 0; i < _n; i++) { + leader_buf[i] = leader(i); + group_size[leader_buf[i]]++; + } + std::vector> result(_n); + for (int i = 0; i < _n; i++) { + result[i].reserve(group_size[i]); + } + for (int i = 0; i < _n; i++) { + result[leader_buf[i]].push_back(i); + } + result.erase( + std::remove_if(result.begin(), result.end(), + [&](const std::vector& v) { return v.empty(); }), + result.end()); + return result; + } + + private: + int _n; + // root node: -1 * component size + // otherwise: parent + std::vector parent_or_size; +}; + +} // namespace atcoder + +// end + +using lint = long long int; + +int main() { + registerValidation(); + + int N = inf.readInt(N_MIN, N_MAX); + inf.readChar(' '); + int Q = inf.readInt(Q_MIN, Q_MAX); + inf.readChar('\n'); + + for (int i = 0; i < N; i++) { + if (i != 0) inf.readChar(' '); + inf.readInt(A_MIN, MOD - 1); + } + inf.readChar('\n'); + + std::vector U(N - 1), V(N - 1); + + for (int i = 0; i < N - 1; i++) { + U[i] = inf.readInt(0, N - 1); + inf.readChar(' '); + V[i] = inf.readInt(0, N - 1); + inf.readChar(' '); + inf.readInt(B_MIN, MOD - 1); + inf.readChar(' '); + inf.readInt(C_MIN, MOD - 1); + inf.readChar('\n'); + } + + for (int i = 0; i < Q; i++) { + int cmd = inf.readInt(0, 1); + inf.readChar(' '); + if (cmd == 0) { + inf.readInt(0, N - 1); + inf.readChar(' '); + inf.readInt(A_MIN, MOD - 1); + inf.readChar(' '); + inf.readInt(0, N - 1); + inf.readChar('\n'); + } else { + inf.readInt(0, N - 2); + inf.readChar(' '); + inf.readInt(B_MIN, MOD - 1); + inf.readChar(' '); + inf.readInt(C_MIN, MOD - 1); + inf.readChar(' '); + inf.readInt(0, N - 1); + inf.readChar('\n'); + } + } + + // check connectivity + atcoder::dsu dsu(N); + for (int i = 0; i < N - 1; i++) { + ensure(!dsu.same(U[i], V[i])); + dsu.merge(U[i], V[i]); + } + + inf.readEof(); + return 0; +} From 2b9f3e31c0b42639d0b3bd0d0a7ebe685a98e603 Mon Sep 17 00:00:00 2001 From: NyaanNyaan Date: Mon, 29 Apr 2024 17:57:47 +0900 Subject: [PATCH 2/2] fix title and forum --- graph/point_set_tree_path_composite_sum/info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graph/point_set_tree_path_composite_sum/info.toml b/graph/point_set_tree_path_composite_sum/info.toml index 53d5d0307..e66b96306 100644 --- a/graph/point_set_tree_path_composite_sum/info.toml +++ b/graph/point_set_tree_path_composite_sum/info.toml @@ -1,6 +1,6 @@ -title = 'Point Set Tree Path Composite Sum (Fixed Root)' +title = 'Point Set Tree Path Composite Sum' timelimit = 10.0 -forum = "https://github.com/yosupo06/library-checker-problems/issues/1136" +forum = "https://github.com/yosupo06/library-checker-problems/issues/1142" [[tests]] name = "example.in"