From 45696cd267bbd3400c86311c17bca7dd461dd7cd Mon Sep 17 00:00:00 2001 From: Misuki743 Date: Sun, 21 Jul 2024 02:50:34 +0800 Subject: [PATCH 1/2] add problem: unionfind with potential --- .../unionfind_with_potential/checker.cpp | 63 ++ .../gen/example_00.in | 11 + .../gen/max_random.cpp | 44 ++ .../unionfind_with_potential/gen/path.cpp | 103 +++ .../unionfind_with_potential/gen/random.cpp | 44 ++ .../unionfind_with_potential/hash.json | 38 ++ .../unionfind_with_potential/info.toml | 25 + .../unionfind_with_potential/sol/acl.h | 592 ++++++++++++++++++ .../unionfind_with_potential/sol/correct.cpp | 78 +++ .../unionfind_with_potential/sol/naive.cpp | 75 +++ .../unionfind_with_potential/task.md | 41 ++ .../unionfind_with_potential/verifier.cpp | 30 + 12 files changed, 1144 insertions(+) create mode 100644 datastructure/unionfind_with_potential/checker.cpp create mode 100644 datastructure/unionfind_with_potential/gen/example_00.in create mode 100644 datastructure/unionfind_with_potential/gen/max_random.cpp create mode 100644 datastructure/unionfind_with_potential/gen/path.cpp create mode 100644 datastructure/unionfind_with_potential/gen/random.cpp create mode 100644 datastructure/unionfind_with_potential/hash.json create mode 100644 datastructure/unionfind_with_potential/info.toml create mode 100644 datastructure/unionfind_with_potential/sol/acl.h create mode 100644 datastructure/unionfind_with_potential/sol/correct.cpp create mode 100644 datastructure/unionfind_with_potential/sol/naive.cpp create mode 100644 datastructure/unionfind_with_potential/task.md create mode 100644 datastructure/unionfind_with_potential/verifier.cpp diff --git a/datastructure/unionfind_with_potential/checker.cpp b/datastructure/unionfind_with_potential/checker.cpp new file mode 100644 index 00000000..6d0e69fa --- /dev/null +++ b/datastructure/unionfind_with_potential/checker.cpp @@ -0,0 +1,63 @@ +// 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/datastructure/unionfind_with_potential/gen/example_00.in b/datastructure/unionfind_with_potential/gen/example_00.in new file mode 100644 index 00000000..2f8a1e52 --- /dev/null +++ b/datastructure/unionfind_with_potential/gen/example_00.in @@ -0,0 +1,11 @@ +4 10 +0 1 0 9 +1 0 2 +0 2 1 90 +0 2 0 99 +0 0 2 123 +0 3 1 990 +1 0 2 +1 2 0 +1 3 0 +1 1 1 diff --git a/datastructure/unionfind_with_potential/gen/max_random.cpp b/datastructure/unionfind_with_potential/gen/max_random.cpp new file mode 100644 index 00000000..d95697c4 --- /dev/null +++ b/datastructure/unionfind_with_potential/gen/max_random.cpp @@ -0,0 +1,44 @@ +#include +#include "random.h" +#include "../sol/acl.h" +#include "../params.h" + +using namespace std; +using namespace atcoder; + +using mint = modint998244353; + +mint id() { return 0; } +mint op(const mint &a, const mint &b) { return a + b; } +mint inv(const mint &a) { return -a; } + +int main(int, char* argv[]) { + + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = gen.uniform(1, N_MAX); + int q = Q_MAX; + + vector A(n); + for(mint &x : A) + x = gen.uniform(0, MOD - 1); + + printf("%d %d\n", n, q); + for (int i = 0; i < q; i++) { + int ty = gen.uniform_bool(); + int a = gen.uniform(0, n - 1); + int b = gen.uniform(0, n - 1); + if (ty == 1) { + printf("%d %d %d\n", ty, a, b); + } else { + mint x; + if (gen.uniform_bool()) + x = gen.uniform(0, MOD - 1); + else + x = op(inv(A[b]), A[a]); + printf("%d %d %d %d\n", ty, a, b, x.val()); + } + } + return 0; +} diff --git a/datastructure/unionfind_with_potential/gen/path.cpp b/datastructure/unionfind_with_potential/gen/path.cpp new file mode 100644 index 00000000..ace7e982 --- /dev/null +++ b/datastructure/unionfind_with_potential/gen/path.cpp @@ -0,0 +1,103 @@ +#include "random.h" +#include "../params.h" +#include "../sol/acl.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace atcoder; + +using mint = modint998244353; + +mint id() { return 0; } +mint op(const mint &a, const mint &b) { return a + b; } +mint inv(const mint &a) { return -a; } + +int main(int, char *argv[]) { + const long long seed = std::atoll(argv[1]); + Random gen(seed); + + const int N_AND_Q = std::min(N_MAX, Q_MAX); + + struct query_type { + int t; + int u; + int v; + }; + + std::vector qs(N_AND_Q); + + // unused-but-set-variable が誤反応するようなので、抑制する +#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif + if (seed == 0 || seed == 1) { + // parent[v] = u 最悪ケース + const int K = 2 * N_AND_Q / 3; + for (int i = 0; i != K; i += 1) { + auto &[t, u, v] = qs[i]; + t = 0; + u = i + 1; + v = 0; + } + for (int i = K; i != N_AND_Q; i += 1) { + auto &[t, u, v] = qs[i]; + t = 1; + u = 0; + v = 1; + } + } + if (seed == 2 || seed == 3) { + // parent[v] = u 深さ最大 + for (int i = 0; i != N_AND_Q - 1; i += 1) { + auto &[t, u, v] = qs[i]; + t = 0; + u = i + 1; + v = i; + } + { + auto &[t, u, v] = qs[N_AND_Q - 1]; + t = 0; + u = 0; + v = N_AND_Q - 1; + } + } + if (seed == 1 || seed == 3) { + // u, v 反転 + for (auto &query : qs) { + std::swap(query.u, query.v); + } + } +#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) +#pragma GCC diagnostic pop +#endif + + vector A(N_AND_Q); + for(mint &x : A) + x = gen.uniform(0, MOD - 1); + + std::vector p(N_AND_Q); + std::iota(p.begin(), p.end(), 0); + gen.shuffle(p.begin(), p.end()); + std::printf("%d %d\n", N_AND_Q, N_AND_Q); + for (const auto &[t, u, v] : qs) { + if (t == 1) { + std::printf("%d %d %d\n", t, p[u], p[v]); + } else { + mint x; + if (gen.uniform_bool()) + x = gen.uniform(0, MOD - 1); + else + x = op(inv(A[v]), A[u]); + std::printf("%d %d %d %d\n", t, p[u], p[v], x.val()); + } + } + + return 0; +} diff --git a/datastructure/unionfind_with_potential/gen/random.cpp b/datastructure/unionfind_with_potential/gen/random.cpp new file mode 100644 index 00000000..a2235132 --- /dev/null +++ b/datastructure/unionfind_with_potential/gen/random.cpp @@ -0,0 +1,44 @@ +#include +#include "random.h" +#include "../sol/acl.h" +#include "../params.h" + +using namespace std; +using namespace atcoder; + +using mint = modint998244353; + +mint id() { return 0; } +mint op(const mint &a, const mint &b) { return a + b; } +mint inv(const mint &a) { return -a; } + +int main(int, char* argv[]) { + + long long seed = atoll(argv[1]); + auto gen = Random(seed); + + int n = gen.uniform(1, N_MAX); + int q = gen.uniform(1, Q_MAX); + + vector A(n); + for(mint &x : A) + x = gen.uniform(0, MOD - 1); + + printf("%d %d\n", n, q); + for (int i = 0; i < q; i++) { + int ty = gen.uniform_bool(); + int a = gen.uniform(0, n - 1); + int b = gen.uniform(0, n - 1); + if (ty == 1) { + printf("%d %d %d\n", ty, a, b); + } else { + mint x; + if (gen.uniform_bool()) + x = gen.uniform(0, MOD - 1); + else + x = op(inv(A[b]), A[a]); + printf("%d %d %d %d\n", ty, a, b, x.val()); + } + } + return 0; +} diff --git a/datastructure/unionfind_with_potential/hash.json b/datastructure/unionfind_with_potential/hash.json new file mode 100644 index 00000000..787ee1cb --- /dev/null +++ b/datastructure/unionfind_with_potential/hash.json @@ -0,0 +1,38 @@ +{ + "example_00.in": "f4fa01a168d9b818f8dbd66f10d4d1c6f4b7f1001fd6bf382c30d23f75298913", + "example_00.out": "ec6959b4802b196473186e786f118528fa08d8eb4c73e3dffcc7404f20330f1f", + "max_random_00.in": "4e44a642c69d8912601aca34d12dc87d7b32827b65808236400ee9de59b50fb1", + "max_random_00.out": "c5997dec97b8b313b64627605f7aa870f5d1aa241cb1c690fda04040d299f31e", + "max_random_01.in": "40006c09d78339e11e97c542b3ff49fedaabeb4a87750a846b1fd8feb2cf45ed", + "max_random_01.out": "3a6f693e0ca0ce15d47019806d92989c691cdbc2ac3da425d518eeec069ad521", + "max_random_02.in": "95bc849d1702d520baf348117740ad9ecee47d0d8db464332222edea19fa72ee", + "max_random_02.out": "8639d7edfe0796281f6437e15ccd79ecc28317f52773f3b93ea4ff194e17d784", + "path_00.in": "e00302d2af330494a4580aeea6334d0b299d02343e91ecfaf0b445339d03877a", + "path_00.out": "5f700d24f95cfa31974d55e9845491aaad07efa4461acd438d40bbe7cc68dbd3", + "path_01.in": "ddf9f1d5e8adf791b88a005e436623b46ceb326764d0a11fabee90380a12d653", + "path_01.out": "17bffd4c31bf4fce60d543dbed7b13b2502291282582145574d30280f1096cec", + "path_02.in": "0362f1bc1f24ddaa06901ed849146e58645267d5c469f0cac09e5b830d80a9a9", + "path_02.out": "92e0ea193ae02c22dcf47b2c3e0b5e48f5a79e773a15eef190898b861773aaf2", + "path_03.in": "95209543e3d7621237e88575f4f6559e397856c6f215b1b4a8e4e038de1de253", + "path_03.out": "92e0ea193ae02c22dcf47b2c3e0b5e48f5a79e773a15eef190898b861773aaf2", + "random_00.in": "175ad842afcc62e2629aa8d0495a0df45d58a594f740a7d0c6e748e87f9ec669", + "random_00.out": "169a46fea9da106bc0ec378539db06ae10c74e28befa80d476bc3d7d9fbe5a11", + "random_01.in": "b95be4cea990478ce62affff56b126daec318c3a27689b125a055212f7ebb09b", + "random_01.out": "befac958d7564504f25830b7849582dea7c48ebb581a3b2eb90ecc2dd5a9a7d9", + "random_02.in": "4118a5833803e881f3ca886aa0553915b68f834f47523c8ec67105967250b317", + "random_02.out": "cc16708c76770b88ad2e8c0e980bce2fc2eea5f18f0a3aeec04e2ba830679181", + "random_03.in": "8da25241ad57a7af4f72686de801c31bb2052a3b5aa270db09a29268554a931c", + "random_03.out": "c8cab222e36c7a03628fa438b7ae60ecafd86500d0ab1fece4b9f240a177a6d8", + "random_04.in": "39bafe10abbf229c763c039951034263e94c27cfdfc433b234eaa32bc1b7140e", + "random_04.out": "4b135c466053ff03409af8ebf982e3ec1b719407ec070028c0d4c743f2429d92", + "random_05.in": "d74b0c6aa235cedb84333ed7b7a3b008c372ef564e553b57dfaeeb252372d9d8", + "random_05.out": "d2eb98a13222c0b02a3530bd92571c471e17ca9508a14b85a7ae5d6e6e825d1f", + "random_06.in": "1eaddcacca38125bb5064001424c10be2a3132c19718f009b1d106791bd7545c", + "random_06.out": "d84d66c6dd7d120eab47ab4a4ca9fed949abe24a18bd17847b84675c05ac75d0", + "random_07.in": "2601d39a88ad5a834177b917b1f2af9e60dce6d4ec47827d24193fbca62d244f", + "random_07.out": "6c52b9ecd7c50f42c4e7508d1c29336cac0b8695e1b2101e0cf2582ad92da4f8", + "random_08.in": "25cf75eea57af237a8ef21b8bccbfa99107681dd619f24eeda8b02315ef45d7a", + "random_08.out": "9916adfa8710a76a826a02a3f1b816e5d6db134745d729af87a32b41f79e2d20", + "random_09.in": "be1b0ba4002df6acccc9bdad202642e93f7d9beec0bb2ecf7beb0f5e3b75d341", + "random_09.out": "ef049e35e660d9dc6c526b4d59998b0b0dba4ade71e58cbcad80007f0f277ffd" +} \ No newline at end of file diff --git a/datastructure/unionfind_with_potential/info.toml b/datastructure/unionfind_with_potential/info.toml new file mode 100644 index 00000000..6493ce28 --- /dev/null +++ b/datastructure/unionfind_with_potential/info.toml @@ -0,0 +1,25 @@ +title = 'Unionfind with Potential' +timelimit = 5.0 +forum = 'https://github.com/yosupo06/library-checker-problems/issues/1194' + +[[tests]] + name = "example.in" + number = 1 +[[tests]] + name = "random.cpp" + number = 10 +[[tests]] + name = "max_random.cpp" + number = 3 +[[tests]] + name = "path.cpp" + number = 4 + +[[solutions]] + name = "naive.cpp" + expect = "TLE" + +[params] + N_MAX = 200_000 + Q_MAX = 200_000 + MOD = 998_244_353 diff --git a/datastructure/unionfind_with_potential/sol/acl.h b/datastructure/unionfind_with_potential/sol/acl.h new file mode 100644 index 00000000..b8cae12c --- /dev/null +++ b/datastructure/unionfind_with_potential/sol/acl.h @@ -0,0 +1,592 @@ +/** + * date : 2024/07/04 + */ + +#ifndef ATCODER_INTERNAL_MATH_HPP +#define ATCODER_INTERNAL_MATH_HPP 1 + +#include + +#ifdef _MSC_VER +#include +#endif + +namespace atcoder { + +namespace internal { + +// @param m `1 <= m` +// @return x mod m +constexpr long long safe_mod(long long x, long long m) { + x %= m; + if (x < 0) x += m; + return x; +} + +// Fast modular multiplication by barrett reduction +// Reference: https://en.wikipedia.org/wiki/Barrett_reduction +// NOTE: reconsider after Ice Lake +struct barrett { + unsigned int _m; + unsigned long long im; + + // @param m `1 <= m` + explicit barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {} + + // @return m + unsigned int umod() const { return _m; } + + // @param a `0 <= a < m` + // @param b `0 <= b < m` + // @return `a * b % m` + unsigned int mul(unsigned int a, unsigned int b) const { + // [1] m = 1 + // a = b = im = 0, so okay + + // [2] m >= 2 + // im = ceil(2^64 / m) + // -> im * m = 2^64 + r (0 <= r < m) + // let z = a*b = c*m + d (0 <= c, d < m) + // a*b * im = (c*m + d) * im = c*(im*m) + d*im = c*2^64 + c*r + d*im + // c*r + d*im < m * m + m * im < m * m + 2^64 + m <= 2^64 + m * (m + 1) < 2^64 * 2 + // ((ab * im) >> 64) == c or c + 1 + unsigned long long z = a; + z *= b; +#ifdef _MSC_VER + unsigned long long x; + _umul128(z, im, &x); +#else + unsigned long long x = + (unsigned long long)(((unsigned __int128)(z)*im) >> 64); +#endif + unsigned long long y = x * _m; + return (unsigned int)(z - y + (z < y ? _m : 0)); + } +}; + +// @param n `0 <= n` +// @param m `1 <= m` +// @return `(x ** n) % m` +constexpr long long pow_mod_constexpr(long long x, long long n, int m) { + if (m == 1) return 0; + unsigned int _m = (unsigned int)(m); + unsigned long long r = 1; + unsigned long long y = safe_mod(x, m); + while (n) { + if (n & 1) r = (r * y) % _m; + y = (y * y) % _m; + n >>= 1; + } + return r; +} + +// Reference: +// M. Forisek and J. Jancina, +// Fast Primality Testing for Integers That Fit into a Machine Word +// @param n `0 <= n` +constexpr bool is_prime_constexpr(int n) { + if (n <= 1) return false; + if (n == 2 || n == 7 || n == 61) return true; + if (n % 2 == 0) return false; + long long d = n - 1; + while (d % 2 == 0) d /= 2; + constexpr long long bases[3] = {2, 7, 61}; + for (long long a : bases) { + long long t = d; + long long y = pow_mod_constexpr(a, t, n); + while (t != n - 1 && y != 1 && y != n - 1) { + y = y * y % n; + t <<= 1; + } + if (y != n - 1 && t % 2 == 0) { + return false; + } + } + return true; +} +template constexpr bool is_prime = is_prime_constexpr(n); + +// @param b `1 <= b` +// @return pair(g, x) s.t. g = gcd(a, b), xa = g (mod b), 0 <= x < b/g +constexpr std::pair inv_gcd(long long a, long long b) { + a = safe_mod(a, b); + if (a == 0) return {b, 0}; + + // Contracts: + // [1] s - m0 * a = 0 (mod b) + // [2] t - m1 * a = 0 (mod b) + // [3] s * |m1| + t * |m0| <= b + long long s = b, t = a; + long long m0 = 0, m1 = 1; + + while (t) { + long long u = s / t; + s -= t * u; + m0 -= m1 * u; // |m1 * u| <= |m1| * s <= b + + // [3]: + // (s - t * u) * |m1| + t * |m0 - m1 * u| + // <= s * |m1| - t * u * |m1| + t * (|m0| + |m1| * u) + // = s * |m1| + t * |m0| <= b + + auto tmp = s; + s = t; + t = tmp; + tmp = m0; + m0 = m1; + m1 = tmp; + } + // by [3]: |m0| <= b/g + // by g != b: |m0| < b/g + if (m0 < 0) m0 += b / s; + return {s, m0}; +} + +// Compile time primitive root +// @param m must be prime +// @return primitive root (and minimum in now) +constexpr int primitive_root_constexpr(int m) { + if (m == 2) return 1; + if (m == 167772161) return 3; + if (m == 469762049) return 3; + if (m == 754974721) return 11; + if (m == 998244353) return 3; + int divs[20] = {}; + divs[0] = 2; + int cnt = 1; + int x = (m - 1) / 2; + while (x % 2 == 0) x /= 2; + for (int i = 3; (long long)(i)*i <= x; i += 2) { + if (x % i == 0) { + divs[cnt++] = i; + while (x % i == 0) { + x /= i; + } + } + } + if (x > 1) { + divs[cnt++] = x; + } + for (int g = 2;; g++) { + bool ok = true; + for (int i = 0; i < cnt; i++) { + if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) { + ok = false; + break; + } + } + if (ok) return g; + } +} +template constexpr int primitive_root = primitive_root_constexpr(m); + +// @param n `n < 2^32` +// @param m `1 <= m < 2^32` +// @return sum_{i=0}^{n-1} floor((ai + b) / m) (mod 2^64) +unsigned long long floor_sum_unsigned(unsigned long long n, + unsigned long long m, + unsigned long long a, + unsigned long long b) { + unsigned long long ans = 0; + while (true) { + if (a >= m) { + ans += n * (n - 1) / 2 * (a / m); + a %= m; + } + if (b >= m) { + ans += n * (b / m); + b %= m; + } + + unsigned long long y_max = a * n + b; + if (y_max < m) break; + // y_max < m * (n + 1) + // floor(y_max / m) <= n + n = (unsigned long long)(y_max / m); + b = (unsigned long long)(y_max % m); + std::swap(m, a); + } + return ans; +} + +} // namespace internal + +} // namespace atcoder + +#endif // ATCODER_INTERNAL_MATH_HPP + +#ifndef ATCODER_INTERNAL_TYPE_TRAITS_HPP +#define ATCODER_INTERNAL_TYPE_TRAITS_HPP 1 + +#include +#include +#include + +namespace atcoder { + +namespace internal { + +#ifndef _MSC_VER +template +using is_signed_int128 = + typename std::conditional::value || + std::is_same::value, + std::true_type, + std::false_type>::type; + +template +using is_unsigned_int128 = + typename std::conditional::value || + std::is_same::value, + std::true_type, + std::false_type>::type; + +template +using make_unsigned_int128 = + typename std::conditional::value, + __uint128_t, + unsigned __int128>; + +template +using is_integral = typename std::conditional::value || + is_signed_int128::value || + is_unsigned_int128::value, + std::true_type, + std::false_type>::type; + +template +using is_signed_int = typename std::conditional<(is_integral::value && + std::is_signed::value) || + is_signed_int128::value, + std::true_type, + std::false_type>::type; + +template +using is_unsigned_int = + typename std::conditional<(is_integral::value && + std::is_unsigned::value) || + is_unsigned_int128::value, + std::true_type, + std::false_type>::type; + +template +using to_unsigned = typename std::conditional< + is_signed_int128::value, + make_unsigned_int128, + typename std::conditional::value, + std::make_unsigned, + std::common_type>::type>::type; + +#else + +template using is_integral = typename std::is_integral; + +template +using is_signed_int = + typename std::conditional::value && std::is_signed::value, + std::true_type, + std::false_type>::type; + +template +using is_unsigned_int = + typename std::conditional::value && + std::is_unsigned::value, + std::true_type, + std::false_type>::type; + +template +using to_unsigned = typename std::conditional::value, + std::make_unsigned, + std::common_type>::type; + +#endif + +template +using is_signed_int_t = std::enable_if_t::value>; + +template +using is_unsigned_int_t = std::enable_if_t::value>; + +template using to_unsigned_t = typename to_unsigned::type; + +} // namespace internal + +} // namespace atcoder + +#endif // ATCODER_INTERNAL_TYPE_TRAITS_HPP + +#ifndef ATCODER_MODINT_HPP +#define ATCODER_MODINT_HPP 1 + +#include +#include +#include + +#ifdef _MSC_VER +#include +#endif + +//#include "atcoder/internal_math" +//#include "atcoder/internal_type_traits" + +namespace atcoder { + +namespace internal { + +struct modint_base {}; +struct static_modint_base : modint_base {}; + +template using is_modint = std::is_base_of; +template using is_modint_t = std::enable_if_t::value>; + +} // namespace internal + +template * = nullptr> +struct static_modint : internal::static_modint_base { + using mint = static_modint; + + public: + static constexpr int mod() { return m; } + static mint raw(int v) { + mint x; + x._v = v; + return x; + } + + static_modint() : _v(0) {} + template * = nullptr> + static_modint(T v) { + long long x = (long long)(v % (long long)(umod())); + if (x < 0) x += umod(); + _v = (unsigned int)(x); + } + template * = nullptr> + static_modint(T v) { + _v = (unsigned int)(v % umod()); + } + + unsigned int val() const { return _v; } + + mint& operator++() { + _v++; + if (_v == umod()) _v = 0; + return *this; + } + mint& operator--() { + if (_v == 0) _v = umod(); + _v--; + return *this; + } + mint operator++(int) { + mint result = *this; + ++*this; + return result; + } + mint operator--(int) { + mint result = *this; + --*this; + return result; + } + + mint& operator+=(const mint& rhs) { + _v += rhs._v; + if (_v >= umod()) _v -= umod(); + return *this; + } + mint& operator-=(const mint& rhs) { + _v -= rhs._v; + if (_v >= umod()) _v += umod(); + return *this; + } + mint& operator*=(const mint& rhs) { + unsigned long long z = _v; + z *= rhs._v; + _v = (unsigned int)(z % umod()); + return *this; + } + mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); } + + mint operator+() const { return *this; } + mint operator-() const { return mint() - *this; } + + mint pow(long long n) const { + assert(0 <= n); + mint x = *this, r = 1; + while (n) { + if (n & 1) r *= x; + x *= x; + n >>= 1; + } + return r; + } + mint inv() const { + if (prime) { + assert(_v); + return pow(umod() - 2); + } else { + auto eg = internal::inv_gcd(_v, m); + assert(eg.first == 1); + return eg.second; + } + } + + friend mint operator+(const mint& lhs, const mint& rhs) { + return mint(lhs) += rhs; + } + friend mint operator-(const mint& lhs, const mint& rhs) { + return mint(lhs) -= rhs; + } + friend mint operator*(const mint& lhs, const mint& rhs) { + return mint(lhs) *= rhs; + } + friend mint operator/(const mint& lhs, const mint& rhs) { + return mint(lhs) /= rhs; + } + friend bool operator==(const mint& lhs, const mint& rhs) { + return lhs._v == rhs._v; + } + friend bool operator!=(const mint& lhs, const mint& rhs) { + return lhs._v != rhs._v; + } + + private: + unsigned int _v; + static constexpr unsigned int umod() { return m; } + static constexpr bool prime = internal::is_prime; +}; + +template struct dynamic_modint : internal::modint_base { + using mint = dynamic_modint; + + public: + static int mod() { return (int)(bt.umod()); } + static void set_mod(int m) { + assert(1 <= m); + bt = internal::barrett(m); + } + static mint raw(int v) { + mint x; + x._v = v; + return x; + } + + dynamic_modint() : _v(0) {} + template * = nullptr> + dynamic_modint(T v) { + long long x = (long long)(v % (long long)(mod())); + if (x < 0) x += mod(); + _v = (unsigned int)(x); + } + template * = nullptr> + dynamic_modint(T v) { + _v = (unsigned int)(v % mod()); + } + + unsigned int val() const { return _v; } + + mint& operator++() { + _v++; + if (_v == umod()) _v = 0; + return *this; + } + mint& operator--() { + if (_v == 0) _v = umod(); + _v--; + return *this; + } + mint operator++(int) { + mint result = *this; + ++*this; + return result; + } + mint operator--(int) { + mint result = *this; + --*this; + return result; + } + + mint& operator+=(const mint& rhs) { + _v += rhs._v; + if (_v >= umod()) _v -= umod(); + return *this; + } + mint& operator-=(const mint& rhs) { + _v += mod() - rhs._v; + if (_v >= umod()) _v -= umod(); + return *this; + } + mint& operator*=(const mint& rhs) { + _v = bt.mul(_v, rhs._v); + return *this; + } + mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); } + + mint operator+() const { return *this; } + mint operator-() const { return mint() - *this; } + + mint pow(long long n) const { + assert(0 <= n); + mint x = *this, r = 1; + while (n) { + if (n & 1) r *= x; + x *= x; + n >>= 1; + } + return r; + } + mint inv() const { + auto eg = internal::inv_gcd(_v, mod()); + assert(eg.first == 1); + return eg.second; + } + + friend mint operator+(const mint& lhs, const mint& rhs) { + return mint(lhs) += rhs; + } + friend mint operator-(const mint& lhs, const mint& rhs) { + return mint(lhs) -= rhs; + } + friend mint operator*(const mint& lhs, const mint& rhs) { + return mint(lhs) *= rhs; + } + friend mint operator/(const mint& lhs, const mint& rhs) { + return mint(lhs) /= rhs; + } + friend bool operator==(const mint& lhs, const mint& rhs) { + return lhs._v == rhs._v; + } + friend bool operator!=(const mint& lhs, const mint& rhs) { + return lhs._v != rhs._v; + } + + private: + unsigned int _v; + static internal::barrett bt; + static unsigned int umod() { return bt.umod(); } +}; +template internal::barrett dynamic_modint::bt(998244353); + +using modint998244353 = static_modint<998244353>; +using modint1000000007 = static_modint<1000000007>; +using modint = dynamic_modint<-1>; + +namespace internal { + +template +using is_static_modint = std::is_base_of; + +template +using is_static_modint_t = std::enable_if_t::value>; + +template struct is_dynamic_modint : public std::false_type {}; +template +struct is_dynamic_modint> : public std::true_type {}; + +template +using is_dynamic_modint_t = std::enable_if_t::value>; + +} // namespace internal + +} // namespace atcoder + +#endif // ATCODER_MODINT_HPP diff --git a/datastructure/unionfind_with_potential/sol/correct.cpp b/datastructure/unionfind_with_potential/sol/correct.cpp new file mode 100644 index 00000000..075a2ce5 --- /dev/null +++ b/datastructure/unionfind_with_potential/sol/correct.cpp @@ -0,0 +1,78 @@ +#include +#include +#include "acl.h" + +using namespace std; +using namespace atcoder; + +template +struct DSU { + vector bos, sz; + vector _pot; + int size; + + DSU(int _size) : bos(_size), sz(_size, 1), _pot(_size, id()), size(_size) { + iota(bos.begin(), bos.end(), 0); + } + + int query(int v) { + if (bos[v] == v) { + return v; + } else { + int tmp = query(bos[v]); + _pot[v] = op(_pot[bos[v]], _pot[v]); + return bos[v] = tmp; + } + } + + //op(v1, d) = v2 + bool merge(int v1, int v2, G d) { + int b1 = query(v1), b2 = query(v2); + + if (b1 == b2) + return op(inv(_pot[v1]), _pot[v2]) == d; + + if (sz[b1] > sz[b2]) { + swap(b1, b2), swap(v1, v2); + d = inv(d); + } + bos[b1] = b2, sz[b2] += sz[b1], _pot[b1] = op(op(_pot[v2], inv(d)), inv(_pot[v1])); + + return true; + } + + //op(inv(v1), v2) + G query(int v1, int v2) { + query(v1), query(v2); + return op(inv(_pot[v1]), _pot[v2]); + } +}; + +using mint = modint998244353; + +mint id() { return 0; } +mint op(const mint &a, const mint &b) { return a + b; } +mint inv(const mint &a) { return -a; } + +int main() { + int N, Q; + scanf("%d %d", &N, &Q); + + DSU dsu(N); + while(Q--) { + int t, u, v; + scanf("%d %d %d", &t, &u, &v); + if (t == 0) { + int x; + scanf("%d", &x); + printf("%d\n", dsu.merge(v, u, mint(x))); + } else { + if (dsu.query(u) != dsu.query(v)) + printf("%d\n", -1); + else + printf("%d\n", dsu.query(v, u).val()); + } + } + + return 0; +} diff --git a/datastructure/unionfind_with_potential/sol/naive.cpp b/datastructure/unionfind_with_potential/sol/naive.cpp new file mode 100644 index 00000000..a8deb942 --- /dev/null +++ b/datastructure/unionfind_with_potential/sol/naive.cpp @@ -0,0 +1,75 @@ +#include +#include +#include "acl.h" + +using namespace std; +using namespace atcoder; + +using mint = modint998244353; + +mint id() { return 0; } +mint op(const mint &a, const mint &b) { return a + b; } +mint inv(const mint &a) { return -a; } + +int main() { + int N, Q; + scanf("%d %d", &N, &Q); + + vector>> g(N); + vector a(N, id()); + vector vis(N); + iota(vis.begin(), vis.end(), 0); + + //op(a[v1], d) = a[v2] + auto try_add = [&](int v1, int v2, mint d) { + g[v1].emplace_back(v2, d); + g[v2].emplace_back(v1, inv(d)); + + bool valid = true; + vector ap(N, id()); + vector visp(N, -1); + int s = 0; + auto dfs = [&](int v, auto &&self) -> void { + visp[v] = s; + for(auto [u, x] : g[v]) { + if (visp[u] != -1) { + valid = valid and op(ap[v], x) == ap[u]; + } else { + ap[u] = op(ap[v], x); + self(u, self); + } + } + }; + for(int v = 0; v < N; v++) if (visp[v] == -1) { + dfs(v, dfs); + s++; + } + + if (valid) { + a.swap(ap); + vis.swap(visp); + } else { + g[v1].pop_back(); + g[v2].pop_back(); + } + + return valid; + }; + + while(Q--) { + int t, u, v; + scanf("%d %d %d", &t, &u, &v); + if (t == 0) { + int x; + scanf("%d", &x); + printf("%d\n", try_add(v, u, mint(x))); + } else { + if (vis[u] != vis[v]) + printf("%d\n", -1); + else + printf("%d\n", op(inv(a[v]), a[u]).val()); + } + } + + return 0; +} diff --git a/datastructure/unionfind_with_potential/task.md b/datastructure/unionfind_with_potential/task.md new file mode 100644 index 00000000..c58b5ad3 --- /dev/null +++ b/datastructure/unionfind_with_potential/task.md @@ -0,0 +1,41 @@ +## @{keyword.statement} + +@{lang.en} + +There is an unknown sequence of integers $(a_0, \ldots, a_{N - 1})$, process $Q$ queries. + +- `$t_i$ = 0 $u_i$ $v_i$ $x_i$`: You are provided with information that $a_u \equiv a_v + x \pmod{@param.MOD}$. This information is valid if it does not contradict any previously given valid information. If this information is valid, output $1$; otherwise, output $0$. + +- `$t_i$ = 1 $u_i$ $v_i$`: Based on the valid information provided so far, output the value of $a_u - a_v \bmod{@param.MOD}$ if it can be determined; otherwise, output $-1$. + +@{lang.ja} + +未知の整数列 $(a_0, \ldots, a_{N - 1})$ がある。$Q$ クエリを処理せよ。 + +- `$t_i$ = 0 $u_i$ $v_i$ $x_i$`:列 $a$ に関して $a_u \equiv a_v + x \pmod{@param.MOD}$ であるという情報が与えられる。この情報が valid であるとは,これまでの valid な情報に矛盾しないことを意味する。この情報が valid ならば $1$、そうでないなら $0$ を出力する。 + +- `$t_i$ = 1 $u_i$ $v_i$`: これまでの valid な情報をもとに +$a_u - a_v \bmod{@param.MOD}$ が定まるならばその値,定まらないならば $-1$ を出力せよ。 + +@{lang.end} + +## @{keyword.constraints} + +- $1 \leq N \leq @{param.N_MAX}$ +- $1 \leq Q \leq @{param.Q_MAX}$ +- $0 \leq u_i, v_i \lt N$ +- $0 \leq x_i \lt @{param.MOD}$ + +## @{keyword.input} + +~~~ +$N$ $Q$ +$\textrm{Query}_0$ +$\textrm{Query}_1$ +: +$\textrm{Query}_{Q - 1}$ +~~~ + +## @{keyword.sample} + +@{example.example_00} diff --git a/datastructure/unionfind_with_potential/verifier.cpp b/datastructure/unionfind_with_potential/verifier.cpp new file mode 100644 index 00000000..1dd87f09 --- /dev/null +++ b/datastructure/unionfind_with_potential/verifier.cpp @@ -0,0 +1,30 @@ +#include "params.h" +#include "testlib.h" + +using namespace std; + +int main() { + registerValidation(); + + int N = inf.readInt(1, N_MAX); + inf.readSpace(); + int Q = inf.readInt(1, Q_MAX); + inf.readChar('\n'); + + while(Q--) { + int t = inf.readInt(0, 1); + inf.readSpace(); + inf.readInt(0, N - 1); + inf.readSpace(); + inf.readInt(0, N - 1); + if (t == 0) { + inf.readSpace(); + inf.readInt(0, MOD - 1); + } + inf.readChar('\n'); + } + + inf.readEof(); + + return 0; +} From 3d1c8630cc1c3eb923dada9bc052d043123a8abd Mon Sep 17 00:00:00 2001 From: Misuki743 Date: Sat, 27 Jul 2024 16:44:51 +0800 Subject: [PATCH 2/2] fix typo --- datastructure/unionfind_with_potential/task.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/datastructure/unionfind_with_potential/task.md b/datastructure/unionfind_with_potential/task.md index c58b5ad3..acdb3a77 100644 --- a/datastructure/unionfind_with_potential/task.md +++ b/datastructure/unionfind_with_potential/task.md @@ -4,18 +4,18 @@ There is an unknown sequence of integers $(a_0, \ldots, a_{N - 1})$, process $Q$ queries. -- `$t_i$ = 0 $u_i$ $v_i$ $x_i$`: You are provided with information that $a_u \equiv a_v + x \pmod{@param.MOD}$. This information is valid if it does not contradict any previously given valid information. If this information is valid, output $1$; otherwise, output $0$. +- `$t_i$ = 0 $u_i$ $v_i$ $x_i$`: You are provided with information that $a_u \equiv a_v + x \pmod{@{param.MOD}}$. This information is valid if it does not contradict any previously given valid information. If this information is valid, output $1$; otherwise, output $0$. -- `$t_i$ = 1 $u_i$ $v_i$`: Based on the valid information provided so far, output the value of $a_u - a_v \bmod{@param.MOD}$ if it can be determined; otherwise, output $-1$. +- `$t_i$ = 1 $u_i$ $v_i$`: Based on the valid information provided so far, output the value of $a_u - a_v \bmod @{param.MOD}$ if it can be determined; otherwise, output $-1$. @{lang.ja} 未知の整数列 $(a_0, \ldots, a_{N - 1})$ がある。$Q$ クエリを処理せよ。 -- `$t_i$ = 0 $u_i$ $v_i$ $x_i$`:列 $a$ に関して $a_u \equiv a_v + x \pmod{@param.MOD}$ であるという情報が与えられる。この情報が valid であるとは,これまでの valid な情報に矛盾しないことを意味する。この情報が valid ならば $1$、そうでないなら $0$ を出力する。 +- `$t_i$ = 0 $u_i$ $v_i$ $x_i$`:列 $a$ に関して $a_u \equiv a_v + x \pmod{@{param.MOD}}$ であるという情報が与えられる。この情報が valid であるとは,これまでの valid な情報に矛盾しないことを意味する。この情報が valid ならば $1$、そうでないなら $0$ を出力する。 - `$t_i$ = 1 $u_i$ $v_i$`: これまでの valid な情報をもとに -$a_u - a_v \bmod{@param.MOD}$ が定まるならばその値,定まらないならば $-1$ を出力せよ。 +$a_u - a_v \bmod @{param.MOD}$ が定まるならばその値,定まらないならば $-1$ を出力せよ。 @{lang.end}