diff --git a/competitive_programming/src/dp/longest_commom_subsequence.hpp b/competitive_programming/src/dp/longest_commom_subsequence.hpp index 7890904..2a8bda1 100644 --- a/competitive_programming/src/dp/longest_commom_subsequence.hpp +++ b/competitive_programming/src/dp/longest_commom_subsequence.hpp @@ -15,7 +15,9 @@ inline std::vector longest_commom_subsequence(const std::vector& first, const std::vector& second) { constexpr int INF = 1e9; - int n = static_cast(first.size()), m = static_cast(second.size()); + const int n = static_cast(first.size()); + const int m = static_cast(second.size()); + std::vector sequence; std::vector> dp(n + 2, std::vector(m + 2, INF)); diff --git a/competitive_programming/src/dp/number_of_subsequences.hpp b/competitive_programming/src/dp/number_of_subsequences.hpp index a7bad07..3875f1b 100644 --- a/competitive_programming/src/dp/number_of_subsequences.hpp +++ b/competitive_programming/src/dp/number_of_subsequences.hpp @@ -10,7 +10,7 @@ namespace dp { */ inline int number_of_subsequences(const std::vector& sequence, int MOD) { - std::size_t n = sequence.size(); + const auto n = sequence.size(); long long dp {}; std::unordered_map DP; diff --git a/competitive_programming/src/dp/rectangle_cutting.hpp b/competitive_programming/src/dp/rectangle_cutting.hpp index 117a34a..cca682b 100644 --- a/competitive_programming/src/dp/rectangle_cutting.hpp +++ b/competitive_programming/src/dp/rectangle_cutting.hpp @@ -11,7 +11,7 @@ namespace dp { * in such a way that all side lengths remain integers. * What is the minimum possible number of moves? */ -inline unsigned rectangle_cutting(unsigned w, unsigned h) +inline unsigned rectangle_cutting(const unsigned w, const unsigned h) { constexpr unsigned INF = 1'000'000'000u; std::vector> dp(w + 1, std::vector(h + 1, INF)); diff --git a/competitive_programming/src/dp/removing_digits.hpp b/competitive_programming/src/dp/removing_digits.hpp index 3bb3a25..f5db33e 100644 --- a/competitive_programming/src/dp/removing_digits.hpp +++ b/competitive_programming/src/dp/removing_digits.hpp @@ -10,7 +10,7 @@ namespace dp * Given an integer n, return the minimum number of steps to reduce it to zero. * In one step, you can remove any digit from n and subtract it from n. */ -inline int removing_digits(int n) +inline int removing_digits(const int n) { std::vector dp(n + 1); diff --git a/competitive_programming/src/geometry/circle.hpp b/competitive_programming/src/geometry/circle.hpp index 45a54b5..990b2b0 100644 --- a/competitive_programming/src/geometry/circle.hpp +++ b/competitive_programming/src/geometry/circle.hpp @@ -21,32 +21,32 @@ struct circle circle(point _c, double _r) : c {_c}, r {_r} {} - double area() const + double area() const { return std::acos(-1) * r * r; } - double chord(double rad) const + double chord(double rad) const { return 2 * r * std::sin(rad / 2); } - double sector(double rad) const + double sector(double rad) const { return 0.5 * rad * area() / std::acos(-1); } - bool intersects(const circle& other) const + bool intersects(const circle& other) const { return dist(c, other.c) < r + other.r; } - bool contains(const point& p) const + bool contains(const point& p) const { return dist(c, p) <= r + EPS; } - std::pair getTangentPoint(const point& p) const + std::pair getTangentPoint(const point& p) const { double d1 = dist(p, c); double theta = std::asin(r / d1); @@ -57,7 +57,7 @@ struct circle return {p1, p2}; } - std::vector> getTangentSegs(const circle& other) const + std::vector> getTangentSegs(const circle& other) const { std::vector> ans; @@ -101,7 +101,7 @@ struct circle } }; -inline circle circumcircle(const point& a, const point& b, const point& c) +inline circle circumcircle(const point& a, const point& b, const point& c) { point u = {(b - a).y, -(b - a).x}; point v = {(c - a).y, -(c - a).x}; @@ -113,14 +113,14 @@ inline circle circumcircle(const point& a, const point& b, const point& c) return ans; } -inline int insideCircle(const point& p, const circle& c) +inline int insideCircle(const point& p, const circle& c) { if(std::fabs(dist(p, c.c) - c.r) < EPS) return 1; else if(dist(p, c.c) < c.r) return 0; return 2; // 0 = inside /1 = border /2 = outside } -inline circle incircle(const point& p1, const point& p2, const point& p3) +inline circle incircle(const point& p1, const point& p2, const point& p3) { double m1 = dist(p2, p3); double m2 = dist(p1, p3); @@ -131,7 +131,7 @@ inline circle incircle(const point& p1, const point& p2, const point& p3) return {c, r}; } -inline circle minimumCircle(std::vector& p) +inline circle minimumCircle(std::vector p) { std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count()); diff --git a/competitive_programming/src/geometry/point.hpp b/competitive_programming/src/geometry/point.hpp index fcc9852..9b4d21b 100644 --- a/competitive_programming/src/geometry/point.hpp +++ b/competitive_programming/src/geometry/point.hpp @@ -13,97 +13,97 @@ struct point point() : x(), y() {} point(double _x, double _y) : x {_x}, y {_y} {} - double norm() const + double norm() const { return std::hypot(x, y); } - point normalized() const + point normalized() const { return point(x, y) * (1.0 / norm()); } - double angle() const + double angle() const { return std::atan2(y, x); } - double polarAngle() const + double polarAngle() const { double a = std::atan2(y, x); return a < 0 ? a + 2 * std::acos(-1) : a; } - bool operator<(const point& other) const + bool operator<(const point& other) const noexcept { if(std::fabs(x - other.x) > EPS) return x < other.x; return y < other.y; } - bool operator>(const point& other) const + bool operator>(const point& other) const noexcept { if(std::fabs(x - other.x) > EPS) return x > other.x; return y > other.y; } - bool operator<=(const point& other) const + bool operator<=(const point& other) const noexcept { if(std::fabs(x - other.x) > EPS) return x < other.x; return y <= other.y; } - bool operator>=(const point& other) const + bool operator>=(const point& other) const noexcept { if(std::fabs(x - other.x) > EPS) return x < other.x; return y >= other.y; } - bool operator==(const point& other) const + bool operator==(const point& other) const noexcept { return std::fabs(x - other.x) < EPS && std::fabs(y - other.y) < EPS; } - bool operator!=(const point& other) const + bool operator!=(const point& other) const noexcept { return !(*this == other); } - point operator+(const point& other) const + point operator+(const point& other) const noexcept { return {x + other.x, y + other.y}; } - point operator-(const point& other) const + point operator-(const point& other) const noexcept { return {x - other.x, y - other.y}; } - point& operator+=(const point& other) + point& operator+=(const point& other) noexcept { x += other.x; y += other.y; return *this; } - point& operator-=(const point& other) + point& operator-=(const point& other) noexcept { x -= other.x; y -= other.y; return *this; } - point& operator*=(double k) + point& operator*=(double k) noexcept { x *= k; y *= k; return *this; } - point operator*(double k) const + point operator*(double k) const noexcept { return {x * k, y * k}; } @@ -120,63 +120,63 @@ struct point return {x / k, y / k}; } - point operator-() const + point operator-() const noexcept { return {-x, -y}; } }; -inline double dist(const point& p1, const point& p2) +inline double dist(const point& p1, const point& p2) { return std::hypot(p1.x - p2.x, p1.y - p2.y); } -inline double inner(const point& p1, const point& p2) +inline double inner(const point& p1, const point& p2) noexcept { return p1.x * p2.x + p1.y * p2.y; } -inline double cross(const point& p1, const point& p2) +inline double cross(const point& p1, const point& p2) noexcept { return p1.x * p2.y - p1.y * p2.x; } -inline bool ccw(const point& p, const point& q, const point& r) +inline bool ccw(const point& p, const point& q, const point& r) noexcept { return cross(q - p, r - p) > EPS; } -inline bool cw(const point& p, const point& q, const point& r) +inline bool cw(const point& p, const point& q, const point& r) noexcept { return cross(q - p, r - p) < -EPS; } -inline bool collinear(const point& p, const point& q, const point& r) +inline bool collinear(const point& p, const point& q, const point& r) noexcept { return std::fabs(cross(p - q, r - p)) < EPS; } -inline point rotate(const point& p, double rad) +inline point rotate(const point& p, double rad) { return {p.x * std::cos(rad) - p.y * std::sin(rad), p.x * std::sin(rad) + p.y * std::cos(rad)}; } -inline double angle(const point& a, const point& o, const point& b) +inline double angle(const point& a, const point& o, const point& b) { return std::acos(inner(a - o, b - o) / dist(o, a) * dist(o, b)); } -inline point proj(const point& u, const point& v) +inline point proj(const point& u, const point& v) { return v * (inner(u, v) / inner(v, v)); } -inline bool between(const point& p, const point& q, const point& r) +inline bool between(const point& p, const point& q, const point& r) noexcept { return collinear(p, q, r) && inner(p - q, r - q) <= 0; } -inline point lineIntersectSeg(const point& p, const point& q, const point& A, const point& B) +inline point lineIntersectSeg(const point& p, const point& q, const point& A, const point& B) { double c = cross(A - B, p - q); double a = cross(A, B); @@ -184,12 +184,12 @@ inline point lineIntersectSeg(const point& p, const point& q, const point& A, co return (p - q) * (a / c) - (A - B) * (b / c); } -inline bool parallel(const point& a, const point& b) +inline bool parallel(const point& a, const point& b) noexcept { return std::fabs(cross(a, b)) < EPS; } -inline bool segIntersects(const point& a, const point& b, const point& p, const point& q) +inline bool segIntersects(const point& a, const point& b, const point& p, const point& q) { if(parallel(a - b, p - q)) return between(a, p, b) || between(a, q, b) || between(p, a, q) || between(p, b, q); @@ -199,7 +199,7 @@ inline bool segIntersects(const point& a, const point& b, const point& p, const return between(a, i, b) && between(p, i, q); } -inline point closestToLineSegment(const point& p, const point& a, const point& b) +inline point closestToLineSegment(const point& p, const point& a, const point& b) { double u = inner(p - a, b - a) / inner(b - a, b - a); if(u < 0) return a; diff --git a/competitive_programming/src/graph/ahu.hpp b/competitive_programming/src/graph/ahu.hpp new file mode 100644 index 0000000..2a4f462 --- /dev/null +++ b/competitive_programming/src/graph/ahu.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "graph/tree_center.hpp" + +namespace graph { + +using adjacency_list = std::vector>; + +class Ahu +{ +public: + Ahu() = default; + + /** + * @brief Find the pattern of a given rooted tree + * @param g The adjacency list + * @param root The root of the tree + */ + int get_rooted_tree_pattern(const adjacency_list& g, int root) + { + assert(!g.empty()); + return dfs(g, root, std::nullopt); + } + + /** + * @brief Find a unique pattern of a given tree + * @param g The adjacency list of the tree + */ + + int get_tree_cannonical_pattern(const adjacency_list& g) + { + int cannonical {}; + + if(g.empty()) + return {}; + + for(const auto v : center(g)) + cannonical = std::max(cannonical, get_rooted_tree_pattern(g, v)); + + return cannonical; + } + +private: + + /** + * @brief Do a DFS from the root to the others nodes + * @param g The adjacency list of the tree + * @param u The node of current subtree + * @param p The parent of the node u + */ + int dfs(const adjacency_list& g, int u, std::optional p) + { + std::vector patterns; + + for(const auto& v : g[u]) + { + if(!p || *p != v) + patterns.emplace_back(dfs(g, v, u)); + } + + std::sort(patterns.begin(), patterns.end()); + + if(auto it = vec2Id.find(patterns); it != vec2Id.end()) + return it->second; + + return vec2Id[patterns] = vec2Id.size(); + } + + /* The map between the array of patterns and the pattern of a subtree */ + std::map, int> vec2Id; +}; + +} \ No newline at end of file diff --git a/competitive_programming/src/graph/centroid.hpp b/competitive_programming/src/graph/centroid.hpp new file mode 100644 index 0000000..dd93c02 --- /dev/null +++ b/competitive_programming/src/graph/centroid.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include +#include + +namespace graph { + +class Centroid +{ +public: + + using adjacency_list = std::vector>; + + Centroid(const adjacency_list& adj) + : _adj(adj) + , size(adj.size(), 0) + { + + } + + int findCentroid() + { + return dfs(0, std::nullopt).value(); + } + +private: + + std::optional dfs(int u, std::optional p) + { + size[u] = 1; + + for(const auto& v : _adj[u]) + { + if(p && *p == v) + continue; + + std::ignore = dfs(v, u); + + size[u] += size[v]; + } + + if(p) return std::nullopt; + + auto answer = std::make_pair(0, 0); + auto parent = std::make_optional(); + + const int n = _adj.size(); + + do + { + answer = std::make_pair(0, 0); + + for(const auto& v : _adj[u]) + { + if(parent && *parent == v) continue; + answer = std::max(answer, std::make_pair(size[v], v)); + } + + parent = u; + u = answer.second; + } while(std::max(answer.first, n - size[*parent]) > n / 2); + + return parent.value(); + } + + const adjacency_list& _adj; + std::vector size; +}; +} \ No newline at end of file diff --git a/competitive_programming/src/graph/dijkstra.hpp b/competitive_programming/src/graph/dijkstra.hpp new file mode 100644 index 0000000..a479f34 --- /dev/null +++ b/competitive_programming/src/graph/dijkstra.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +namespace graph { + +using result_type = std::optional; +using adjacency_list = std::vector>>; + +inline std::vector dijkstra(const adjacency_list& adj, std::size_t start) +{ + using pq_type = std::pair; + + const auto n = adj.size(); + + std::vector dist(n); + std::priority_queue, std::greater> pq; + + pq.emplace(0LL, start); + + while(!pq.empty()) + { + auto [cost, start] = pq.top(); + + pq.pop(); + + if(dist[start].has_value()) + continue; + + dist[start] = cost; + + for(const auto& [dest, weight] : adj[start]) + { + if(dist[dest].has_value()) + continue; + + pq.emplace(cost + weight, dest); + } + } + + return dist; +} + +} \ No newline at end of file diff --git a/competitive_programming/src/graph/tree_center.hpp b/competitive_programming/src/graph/tree_center.hpp index 121acbd..8d95639 100644 --- a/competitive_programming/src/graph/tree_center.hpp +++ b/competitive_programming/src/graph/tree_center.hpp @@ -5,7 +5,7 @@ #include /** - * @brief header that compute the center and diameter of a tree + * @brief Find the center and diameter of a tree */ namespace graph diff --git a/competitive_programming/src/math/nim.hpp b/competitive_programming/src/math/nim.hpp new file mode 100644 index 0000000..d443193 --- /dev/null +++ b/competitive_programming/src/math/nim.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace math { + +/** + * @brief nim game solution + * @param coins the coins that will be used in the game + */ +inline bool nim(const std::vector& coins) +{ + int result = std::accumulate(coins.begin(), coins.end(), 0, [](auto lhs, auto rhs) { + return lhs ^ rhs; + }); + + return result != 0; +} +} diff --git a/competitive_programming/src/math/sieve.hpp b/competitive_programming/src/math/sieve.hpp new file mode 100644 index 0000000..72b4544 --- /dev/null +++ b/competitive_programming/src/math/sieve.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace math { + +/** + * @brief Find all primes less or equal than n + */ +inline std::vector sieve(int n) +{ + std::vector is_prime(n + 1, true); + std::vector primes; + + for(int p = 2; p * p <= n; ++p) + { + if(!is_prime[p]) + continue; + + for(int i = 2 * p; i <= n; i += p) + is_prime[i] = false; + } + + for(int p = 2; p <= n; ++p) + { + if(is_prime[p]) + primes.emplace_back(p); + } + + return primes; +} + +} \ No newline at end of file diff --git a/competitive_programming/src/math/staircase.hpp b/competitive_programming/src/math/staircase.hpp new file mode 100644 index 0000000..8c3ac66 --- /dev/null +++ b/competitive_programming/src/math/staircase.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include + +namespace math { + +/** + * @brief staircase nim game solution + * @param coins the coins that will be used in the game + */ +inline bool staircase(const std::vector& coins) +{ + const auto n = coins.size(); + + auto view = std::views::iota(std::size_t {}, n) | std::views::filter([](auto idx) { + return idx % 2 == 1; + }) | std::views::transform([&coins](auto idx) { + return coins[idx]; + }); + + int result = std::reduce(std::execution::seq, std::ranges::begin(view), std::ranges::end(view), 0, [](auto lhs, auto rhs) { + return lhs ^ rhs; + }); + + return result != 0; +} +} diff --git a/competitive_programming/src/math/sum_floor.hpp b/competitive_programming/src/math/sum_floor.hpp new file mode 100644 index 0000000..7bfd8d4 --- /dev/null +++ b/competitive_programming/src/math/sum_floor.hpp @@ -0,0 +1,39 @@ +#pragma once + +namespace math { + +using value_type = long long; + +/** + * @brief Find \sum_{i=0}^{n-1} \left\lfloor \frac{a i + b}{m} \right\rfloor + */ +inline value_type sum_floor(value_type n, value_type m, value_type a, value_type b) +{ + auto linSum = [](auto l, auto r) + { + return (r - l + 1) * (l + r) / 2; + }; + + auto sum = [linSum](auto& self, auto n, auto m, auto a, auto b) + { + if(!a) + return b * n; + + long long k = (a * (n - 1) + b) / m; + + __int128 s = b * n + static_cast<__int128>(a) * linSum(0, n - 1); + + __int128 t = k * (n - 1) + - ((a + (-b - 1) % a) % a + + static_cast<__int128>(m) * linSum(1, k) + - static_cast<__int128>(k) * (b + 1) + - self(self, k + 1, a, m % a, (a + (-b - 1) % a) % a)) / a; + + return static_cast(s - m * t); + }; + + auto s = static_cast<__int128>(b) * n + static_cast<__int128>(a) * linSum(0, n - 1) - sum(sum, n, m, a, b); + + return static_cast(s / m); +} +} \ No newline at end of file diff --git a/competitive_programming/src/math/totient.hpp b/competitive_programming/src/math/totient.hpp new file mode 100644 index 0000000..d2f50ec --- /dev/null +++ b/competitive_programming/src/math/totient.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +namespace math { + +inline int totient(int n) +{ + int result = 1; + + assert(n > 0); + + if(~n & 1) + { + do + { + n >>= 1; + result <<= 1; + } while (~n & 1); + + result >>= 1; + } + + for(int k = 3; k * k <= n; k += 2) + { + if(n % k) + continue; + + do + { + n /= k; + result *= k; + } while (n % k == 0); + + result = result / k * (k - 1); + } + + if(n > 1) + result *= n - 1; + + return result; +} +} \ No newline at end of file diff --git a/competitive_programming/src/string/kmp.hpp b/competitive_programming/src/string/kmp.hpp index 4d8cb63..89541b8 100644 --- a/competitive_programming/src/string/kmp.hpp +++ b/competitive_programming/src/string/kmp.hpp @@ -3,25 +3,34 @@ #include #include -namespace strings { +namespace string { +/** + * @brief KMP algorithm + */ inline std::vector kmp(const std::string& s) { - int n = s.size(); + const auto n = static_cast(s.size()); if(n == 0) return {}; std::vector p(n); + p[0] = 0; - for(int i = 1; i < n; ++i) { + + for(int i = 1; i < n; ++i) + { int j = p[i - 1]; + while(j && s[i] != s[j]) j = p[j - 1]; + p[i] = j; + if(s[i] == s[j]) ++p[i]; } + return p; } - } diff --git a/competitive_programming/src/string/manacher.hpp b/competitive_programming/src/string/manacher.hpp new file mode 100644 index 0000000..eaf4b29 --- /dev/null +++ b/competitive_programming/src/string/manacher.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +namespace string { + +/** + * @brief Manacher Algorithm to find the longest palindrome length + */ +inline int manacher(const std::string& s) +{ + const auto n = static_cast(s.size()); + + std::vector d1(n, 1), d2(n, 0); + + int l = 0, r = -1, len = 0; + + for(int i = 0; i < n; ++i) + { + if(i <= r) + { + d1[i] = std::min(d1[l + r - i], r + 1 - i); + d2[i] = std::min(d2[l + r + 1 - i], r + 1 - i); + } + + while(i + d1[i] < n && i >= d1[i] && s[i - d1[i]] == s[i + d1[i]]) ++d1[i]; + while(i + d2[i] < n && i > d2[i] && s[i - d2[i] - 1] == s[i + d2[i]]) ++d2[i]; + + if(r < i + d2[i] - 1) l = i - d2[i], r = i + d2[i] - 1; + if(r < i + d1[i] - 1) l = i + 1 - d1[i], r = i + d1[i] - 1; + + len = std::max(len, r + 1 - l); + } + + return len; +} +} \ No newline at end of file diff --git a/competitive_programming/src/string/z_function.hpp b/competitive_programming/src/string/z_function.hpp index 69b0c11..33ddbea 100644 --- a/competitive_programming/src/string/z_function.hpp +++ b/competitive_programming/src/string/z_function.hpp @@ -4,17 +4,26 @@ #include #include -namespace strings { +namespace string { -inline std::vector z_function(const std::string& s) { - int n = (int)s.length(); +/** + * @brief Z Function algorithm + */ +inline std::vector z_function(const std::string& s) +{ + const auto n = static_cast(s.length()); + std::vector z(n, 0); - for (int i = 1, l = 0, r = 0; i < n; ++i) { + + for (int i = 1, l = 0, r = 0; i < n; ++i) + { if(i <= r) z[i] = std::min(r - i + 1, z[i - l]); + while(i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i]; + if(i + z[i] - 1 > r) l = i, r = i + z[i] - 1; } + return z; } - } diff --git a/competitive_programming/test/graph/CMakeLists.txt b/competitive_programming/test/graph/CMakeLists.txt index 2fc3cff..5bd83c6 100644 --- a/competitive_programming/test/graph/CMakeLists.txt +++ b/competitive_programming/test/graph/CMakeLists.txt @@ -1,9 +1,26 @@ +# Tree Center add_executable(tree-center tree_center.cpp) - target_include_directories(tree-center PRIVATE ${PROJECT_INCLUDE_DIR}) - target_link_libraries(tree-center PRIVATE GTest::gtest GTest::gtest_main) +# Ahu +add_executable(ahu ahu.cpp) +target_include_directories(ahu PRIVATE ${PROJECT_INCLUDE_DIR}) +target_link_libraries(ahu PRIVATE GTest::gtest GTest::gtest_main) + +# Dijkstra +add_executable(dijkstra dijkstra.cpp) +target_include_directories(dijkstra PRIVATE ${PROJECT_INCLUDE_DIR}) +target_link_libraries(dijkstra PRIVATE GTest::gtest GTest::gtest_main) + +# Centroid +add_executable(centroid centroid.cpp) +target_include_directories(centroid PRIVATE ${PROJECT_INCLUDE_DIR}) +target_link_libraries(centroid PRIVATE GTest::gtest GTest::gtest_main) + include(GoogleTest) -gtest_discover_tests(tree-center) \ No newline at end of file +gtest_discover_tests(tree-center) +gtest_discover_tests(ahu) +gtest_discover_tests(dijkstra) +gtest_discover_tests(centroid) \ No newline at end of file diff --git a/competitive_programming/test/graph/ahu.cpp b/competitive_programming/test/graph/ahu.cpp new file mode 100644 index 0000000..f5b939d --- /dev/null +++ b/competitive_programming/test/graph/ahu.cpp @@ -0,0 +1,143 @@ +#include + +#include "graph/ahu.hpp" + +#include + +using namespace graph; + +void add_edge(adjacency_list& g, int u, int v) +{ + g[u].emplace_back(v); + g[v].emplace_back(u); +} + +adjacency_list build_tree(const std::vector& parents) +{ + const auto n = parents.size() + 1; + adjacency_list g(n); + + for(std::size_t i = 0; i < parents.size(); ++i) + { + int u = i + 1; + int v = parents[i] - 1; + + add_edge(g, u, v); + } + + return g; +} + +TEST(Ahu, EmptyTreesTest) +{ + adjacency_list adj1, adj2; + + Ahu ahu; + + auto pattern_adj_1 = ahu.get_tree_cannonical_pattern(adj1); + auto pattern_adj_2 = ahu.get_tree_cannonical_pattern(adj2); + + EXPECT_EQ(pattern_adj_1, 0); + EXPECT_EQ(pattern_adj_1, pattern_adj_2); +} + +TEST(Ahu, StarTreeTest) +{ + constexpr std::size_t N = 8; + + adjacency_list adj1(N), adj2(N); + Ahu ahu; + + for(std::size_t i = 0; i < N; ++i) + { + if(i != 0) + add_edge(adj1, 0, i); + + if(i != 1) + add_edge(adj2, 1, i); + } + + auto pattern_adj_1 = ahu.get_tree_cannonical_pattern(adj1); + auto pattern_adj_2 = ahu.get_tree_cannonical_pattern(adj2); + + EXPECT_EQ(pattern_adj_1, pattern_adj_2); +} + +TEST(Ahu, DifferentTreesTest) +{ + constexpr std::size_t N = 8; + + adjacency_list adj1(N), adj2(N); + Ahu ahu; + + for(std::size_t i = 1; i < N; ++i) + { + add_edge(adj1, 0, i); + add_edge(adj2, i - 1, i); + } + + auto pattern_adj_1 = ahu.get_tree_cannonical_pattern(adj1); + auto pattern_adj_2 = ahu.get_tree_cannonical_pattern(adj2); + + EXPECT_NE(pattern_adj_1, pattern_adj_2); +} + +TEST(Ahu, CountUniqueTreesPatterns) +{ + std::vector> data = { + {1, 1, 1, 1, 3}, + {1, 2, 1, 2, 5}, + {1, 2, 1, 2, 4}, + {1, 2, 3, 1, 1}, + {1, 2, 2, 1, 4}, + {1, 2, 1, 3, 5}, + {1, 2, 1, 3, 5}, + {1, 1, 2, 4, 2}, + {1, 1, 1, 2, 4}, + {1, 2, 1, 4, 1}, + {1, 2, 2, 3, 4}, + {1, 2, 1, 3, 4}, + {1, 1, 1, 3, 4}, + {1, 2, 3, 1, 2}, + {1, 2, 3, 1, 2}, + {1, 1, 3, 2, 5}, + {1, 1, 2, 1, 3}, + {1, 2, 1, 3, 1}, + {1, 1, 3, 3, 5}, + {1, 1, 3, 3, 3}, + {1, 2, 1, 2, 3}, + {1, 2, 2, 4, 3}, + {1, 1, 2, 1, 3}, + {1, 2, 2, 3, 1}, + {1, 1, 3, 3, 1}, + {1, 2, 2, 2, 1}, + {1, 2, 3, 4, 4}, + {1, 1, 2, 3, 1}, + {1, 2, 3, 3, 3}, + {1, 2, 1, 2, 3}, + {1, 1, 3, 2, 3}, + {1, 2, 2, 4, 1}, + {1, 1, 1, 1, 5}, + {1, 2, 3, 3, 4}, + {1, 2, 3, 2, 2}, + {1, 1, 2, 2, 4}, + {1, 2, 3, 4, 2}, + {1, 2, 2, 2, 1}, + {1, 1, 2, 2, 5}, + {1, 1, 2, 3, 5} + }; + + constexpr std::size_t expected_trees = 5; + + Ahu ahu; + std::unordered_set set; + + for(const auto& parents : data) + { + auto g = build_tree(parents); + auto pattern = ahu.get_tree_cannonical_pattern(g); + set.emplace(pattern); + } + + EXPECT_EQ(set.size(), expected_trees); +} \ No newline at end of file diff --git a/competitive_programming/test/graph/centroid.cpp b/competitive_programming/test/graph/centroid.cpp new file mode 100644 index 0000000..306a5bd --- /dev/null +++ b/competitive_programming/test/graph/centroid.cpp @@ -0,0 +1,206 @@ +#include + +#include "graph/centroid.hpp" + +using adjacenty_list = graph::Centroid::adjacency_list; + +adjacenty_list build_tree(const std::vector>& edges) +{ + const auto n = edges.size() + 1; + + adjacenty_list adj(n); + + for(const auto& [u, v] : edges) + { + adj[u - 1].emplace_back(v - 1); + adj[v - 1].emplace_back(u - 1); + } + + return adj; +} + +TEST(Centroid, CentroidTestSample1) +{ + std::vector> edges { + {4, 1}, + {6, 5}, + {7, 2}, + {6, 3}, + {1, 7}, + {2, 10}, + {10, 9}, + {3, 8}, + {8, 9} + }; + + auto adj = build_tree(edges); + + graph::Centroid centroindFinder(adj); + + int centroid = centroindFinder.findCentroid(); + + EXPECT_TRUE(centroid == 8 || centroid == 9); +} + +TEST(Centroid, CentroidTestSample2) +{ + std::vector> edges = { + {8, 5}, + {7, 4}, + {2, 6}, + {3, 9}, + {6, 8}, + {4, 1}, + {3, 10}, + {5, 10}, + {1, 2} + }; + + auto adj = build_tree(edges); + + graph::Centroid centroindFinder(adj); + + int centroid = centroindFinder.findCentroid(); + + EXPECT_TRUE(centroid == 5 || centroid == 7); +} + +TEST(Centroid, CentroidTestSample3) +{ + std::vector> edges = { + {6, 4}, + {1, 3}, + {10, 8}, + {9, 3}, + {2, 7}, + {5, 4}, + {2, 4}, + {8, 5}, + {9, 5} + }; + + auto adj = build_tree(edges); + + graph::Centroid centroindFinder(adj); + + int centroid = centroindFinder.findCentroid(); + + EXPECT_TRUE(centroid == 4 || centroid == 8); +} + +TEST(Centroid, CentroidTestSample4) +{ + std::vector> edges = { + {3, 6}, + {9, 1}, + {4, 5}, + {2, 9}, + {7, 6}, + {8, 4}, + {3, 5}, + {10, 1}, + {4, 1} + }; + + auto adj = build_tree(edges); + + graph::Centroid centroindFinder(adj); + + int centroid = centroindFinder.findCentroid(); + + EXPECT_TRUE(centroid == 0 || centroid == 3); +} + +TEST(Centroid, CentroidTestSample5) +{ + std::vector> edges = { + {3, 1}, + {9, 8}, + {6, 5}, + {2, 9}, + {4, 7}, + {3, 4}, + {3, 9}, + {5, 3}, + {6, 10} + }; + + auto adj = build_tree(edges); + + graph::Centroid centroindFinder(adj); + + int centroid = centroindFinder.findCentroid(); + + EXPECT_TRUE(centroid == 2); +} + +TEST(Centroid, CentroidTestSample6) +{ + std::vector> edges = {{1, 2}}; + + auto adj = build_tree(edges); + + graph::Centroid centroindFinder(adj); + + int centroid = centroindFinder.findCentroid(); + + EXPECT_TRUE(centroid == 0 || centroid == 1); +} + +TEST(Centroid, CentroidTestSample7) +{ + std::vector> edges = { + {1, 2}, + {2, 3}, + {3, 4}, + {4, 5} + }; + + auto adj = build_tree(edges); + + graph::Centroid centroindFinder(adj); + + int centroid = centroindFinder.findCentroid(); + + EXPECT_TRUE(centroid == 2); +} + +TEST(Centroid, CentroidTestSample8) +{ + std::vector> edges = { + {1, 5}, + {5, 4}, + {4, 3}, + {3, 2} + }; + + auto adj = build_tree(edges); + + graph::Centroid centroindFinder(adj); + + int centroid = centroindFinder.findCentroid(); + + EXPECT_TRUE(centroid == 3); +} + +TEST(Centroid, CentroidTestSample9) +{ + std::vector> edges = { + {1, 2}, + {2, 3}, + {1, 4}, + {4, 5}, + {5, 6}, + {2, 7}, + {2, 8}, + {2, 9} + }; + + auto adj = build_tree(edges); + + graph::Centroid centroindFinder(adj); + + int centroid = centroindFinder.findCentroid(); + + EXPECT_TRUE(centroid == 1); +} \ No newline at end of file diff --git a/competitive_programming/test/graph/dijkstra.cpp b/competitive_programming/test/graph/dijkstra.cpp new file mode 100644 index 0000000..d8e33bc --- /dev/null +++ b/competitive_programming/test/graph/dijkstra.cpp @@ -0,0 +1,80 @@ +#include + +#include "graph/dijkstra.hpp" + +#include +#include + +using namespace graph; + +void add_edge(adjacency_list& adj, std::size_t u, std::size_t v, long long weight) +{ + adj[u].emplace_back(v, weight); + adj[v].emplace_back(u, weight); +} + +TEST(Dijkstra, MultipleSimplePathTreeTest) +{ + constexpr std::size_t N = 8; + + adjacency_list adj(N); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution uid(static_cast(1e5), static_cast(1e9)); + + for(std::size_t i = 1; i < N; ++i) + { + add_edge(adj, i - 1, i, i); + add_edge(adj, i - 1, i, uid(gen)); + } + + auto dist = dijkstra(adj, 0); + long long total_dist {}; + + EXPECT_EQ(dist.size(), N); + + for(std::size_t i = 0; i < N; ++i) + { + total_dist += i; + + EXPECT_TRUE(static_cast(dist[i])); + EXPECT_EQ(*dist[i], total_dist); + } +} + +TEST(Dijkstra, CompleteGraphTest) +{ + constexpr std::size_t N = 8; + constexpr std::size_t source = 3; + + adjacency_list adj(N); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution uid(static_cast(1e5), static_cast(1e9)); + + for(std::size_t i = 0; i < N; ++i) + { + for(std::size_t j = i + 1; j < N; ++j) + { + if(i == source || j == source) + add_edge(adj, i, j, 1); + else + add_edge(adj, i, j, uid(gen)); + } + } + + auto dist = dijkstra(adj, source); + + EXPECT_EQ(dist.size(), N); + + for(std::size_t i = 0; i < N; ++i) + { + EXPECT_TRUE(static_cast(dist[i])); + + if(i == source) + EXPECT_EQ(*dist[i], 0); + else + EXPECT_EQ(*dist[i], 1); + } +} \ No newline at end of file diff --git a/competitive_programming/test/math/CMakeLists.txt b/competitive_programming/test/math/CMakeLists.txt index 1c97503..0ba18f6 100644 --- a/competitive_programming/test/math/CMakeLists.txt +++ b/competitive_programming/test/math/CMakeLists.txt @@ -28,6 +28,31 @@ add_executable(fft fft.cpp) target_include_directories(fft PRIVATE ${PROJECT_INCLUDE_DIR}) target_link_libraries(fft PRIVATE GTest::gtest GTest::gtest_main) +# Totient +add_executable(totient totient.cpp) +target_include_directories(totient PRIVATE ${PROJECT_INCLUDE_DIR}) +target_link_libraries(totient PRIVATE GTest::gtest GTest::gtest_main) + +# Sieve +add_executable(sieve sieve.cpp) +target_include_directories(sieve PRIVATE ${PROJECT_INCLUDE_DIR}) +target_link_libraries(sieve PRIVATE GTest::gtest GTest::gtest_main) + +# StairCase +add_executable(staircase staircase.cpp) +target_include_directories(staircase PRIVATE ${PROJECT_INCLUDE_DIR}) +target_link_libraries(staircase PRIVATE GTest::gtest GTest::gtest_main) + +# Nim +add_executable(nim nim.cpp) +target_include_directories(nim PRIVATE ${PROJECT_INCLUDE_DIR}) +target_link_libraries(nim PRIVATE GTest::gtest GTest::gtest_main) + +# Sum Floor +add_executable(sum_floor sum_floor.cpp) +target_include_directories(sum_floor PRIVATE ${PROJECT_INCLUDE_DIR}) +target_link_libraries(sum_floor PRIVATE GTest::gtest GTest::gtest_main) + include(GoogleTest) gtest_discover_tests(bin-exp) @@ -35,4 +60,9 @@ gtest_discover_tests(miller-rabin) gtest_discover_tests(euclides-ext) gtest_discover_tests(mint) gtest_discover_tests(pollard-rho) -gtest_discover_tests(fft) \ No newline at end of file +gtest_discover_tests(fft) +gtest_discover_tests(totient) +gtest_discover_tests(sieve) +gtest_discover_tests(staircase) +gtest_discover_tests(nim) +gtest_discover_tests(sum_floor) \ No newline at end of file diff --git a/competitive_programming/test/math/nim.cpp b/competitive_programming/test/math/nim.cpp new file mode 100644 index 0000000..59812ba --- /dev/null +++ b/competitive_programming/test/math/nim.cpp @@ -0,0 +1,72 @@ +#include + +#include "math/nim.hpp" + +#include +#include +#include +#include + +TEST(Nim, LoseNimTest) +{ + const std::size_t N[] = {10, 50, 100}; + + for(const auto n : N) + { + std::vector coins; + int coins_xor {}; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution uid(0, std::numeric_limits::max()); + + auto view = std::views::iota(std::size_t {}, n) | std::views::transform([&](auto idx) -> int { + if(idx == n - 1) + return coins_xor; + else + { + auto value = uid(gen); + coins_xor ^= value; + return value; + } + }); + + std::ranges::copy(view, std::back_inserter(coins)); + + bool is_first_winner = math::nim(coins); + + EXPECT_FALSE(is_first_winner); + } +} + +TEST(Nim, WinNimTest) +{ + const std::size_t N[] = {10, 50, 100}; + + for(const auto n : N) + { + std::vector coins; + int coins_xor {}; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution uid(1, std::numeric_limits::max()); + + auto view = std::views::iota(std::size_t {}, n) | std::views::transform([&](auto idx) { + if(idx == n - 1) + return coins_xor ^ uid(gen); + else + { + auto value = uid(gen); + coins_xor ^= value; + return value; + } + }); + + std::ranges::copy(view, std::back_inserter(coins)); + + bool is_first_winner = math::nim(coins); + + EXPECT_TRUE(is_first_winner); + } +} \ No newline at end of file diff --git a/competitive_programming/test/math/sieve.cpp b/competitive_programming/test/math/sieve.cpp new file mode 100644 index 0000000..e860cbd --- /dev/null +++ b/competitive_programming/test/math/sieve.cpp @@ -0,0 +1,23 @@ +#include + +#include "math/sieve.hpp" +#include "math/pollard_rho.hpp" + +#include + +using namespace math; + +TEST(Sieve, PrimesTest) +{ + constexpr int N = 1000; + + auto primes = sieve(N); + + auto view = std::views::iota(2, N + 1) | std::views::filter([](int n) { + return factorization::miller(n); + }); + + std::vector numbers(std::ranges::begin(view), std::ranges::end(view)); + + EXPECT_EQ(primes, numbers); +} \ No newline at end of file diff --git a/competitive_programming/test/math/staircase.cpp b/competitive_programming/test/math/staircase.cpp new file mode 100644 index 0000000..470df1a --- /dev/null +++ b/competitive_programming/test/math/staircase.cpp @@ -0,0 +1,79 @@ +#include + +#include "math/staircase.hpp" + +#include +#include +#include +#include + +TEST(Staircase, LoseStairCaseTest) +{ + const std::size_t N[] = {10, 50, 100}; + + for(const auto n : N) + { + std::vector coins; + int coins_xor {}; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution uid(0, std::numeric_limits::max()); + + std::ranges::for_each(std::views::iota(std::size_t {}, n), [&](auto idx) { + if(idx & 1) + { + if(idx == n - ~n % 2) + coins.push_back(coins_xor); + else + { + int value = uid(gen); + coins.push_back(value); + coins_xor ^= value; + } + } + else + coins.push_back(uid(gen)); + }); + + bool is_first_winner = math::staircase(coins); + + EXPECT_FALSE(is_first_winner); + } +} + +TEST(Staircase, WinStairCaseTest) +{ + const std::size_t N[] = {10, 50, 100}; + + for(const auto n : N) + { + std::vector coins; + int coins_xor {}; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution uid(1, std::numeric_limits::max()); + + std::ranges::for_each(std::views::iota(std::size_t {}, n), [&](auto idx) { + if(idx & 1) + { + int value = uid(gen); + + if(idx == n - ~n % 2) + coins.push_back(coins_xor ^ value); + else + { + coins.push_back(value); + coins_xor ^= value; + } + } + else + coins.push_back(uid(gen)); + }); + + bool is_first_winner = math::staircase(coins); + + EXPECT_TRUE(is_first_winner); + } +} \ No newline at end of file diff --git a/competitive_programming/test/math/sum_floor.cpp b/competitive_programming/test/math/sum_floor.cpp new file mode 100644 index 0000000..699e993 --- /dev/null +++ b/competitive_programming/test/math/sum_floor.cpp @@ -0,0 +1,15 @@ +#include + +#include "math/sum_floor.hpp" + +TEST(SumFloor, TestPairs) +{ + EXPECT_EQ(math::sum_floor(4LL, 10LL, 6LL, 3LL), 3LL); + EXPECT_EQ(math::sum_floor(6LL, 5LL, 4LL, 3LL), 13LL); + EXPECT_EQ(math::sum_floor(1LL, 1LL, 0LL, 0LL), 0LL); + EXPECT_EQ(math::sum_floor(31415LL, 92653LL, 58979LL, 32384LL), 314095480LL); + EXPECT_EQ( + math::sum_floor(1000000000LL, 1000000000LL, 999999999LL, 999999999LL), + 499999999500000000LL + ); +} \ No newline at end of file diff --git a/competitive_programming/test/math/totient.cpp b/competitive_programming/test/math/totient.cpp new file mode 100644 index 0000000..70e5812 --- /dev/null +++ b/competitive_programming/test/math/totient.cpp @@ -0,0 +1,15 @@ +#include + +#include "math/totient.hpp" + +TEST(Totient, TestTotientPairs) +{ + EXPECT_EQ(math::totient(2), 1); + EXPECT_EQ(math::totient(5), 4); + EXPECT_EQ(math::totient(7), 6); + EXPECT_EQ(math::totient(2 * 3), 2); + EXPECT_EQ(math::totient(2 * 3 * 5), 8); + EXPECT_EQ(math::totient(2 * 2), 2); + EXPECT_EQ(math::totient(2 * 2 * 3 * 3), 12); + EXPECT_EQ(math::totient(2 * 2 * 3 * 3 * 5), 48); +} \ No newline at end of file diff --git a/competitive_programming/test/string/CMakeLists.txt b/competitive_programming/test/string/CMakeLists.txt index 28b3292..ff6e19a 100644 --- a/competitive_programming/test/string/CMakeLists.txt +++ b/competitive_programming/test/string/CMakeLists.txt @@ -8,7 +8,13 @@ add_executable(kmp kmp.cpp) target_include_directories(kmp PRIVATE ${PROJECT_INCLUDE_DIR}) target_link_libraries(kmp PRIVATE GTest::gtest GTest::gtest_main) +# Manacher +add_executable(manacher manacher.cpp) +target_include_directories(manacher PRIVATE ${PROJECT_INCLUDE_DIR}) +target_link_libraries(manacher PRIVATE GTest::gtest GTest::gtest_main) + include(GoogleTest) gtest_discover_tests(z-function) gtest_discover_tests(kmp) +gtest_discover_tests(manacher) \ No newline at end of file diff --git a/competitive_programming/test/string/kmp.cpp b/competitive_programming/test/string/kmp.cpp index 0a456f4..d13f50a 100644 --- a/competitive_programming/test/string/kmp.cpp +++ b/competitive_programming/test/string/kmp.cpp @@ -7,7 +7,7 @@ TEST(KMP, Palindrome) const std::string word = "abcbcba"; const std::vector expected {0, 0, 0, 0, 0, 0, 1}; - const auto kmp = strings::kmp(word); + const auto kmp = string::kmp(word); EXPECT_EQ(kmp, expected); } @@ -17,7 +17,7 @@ TEST(KMP, Repeated) const std::string word = "abcabcabc"; const std::vector expected {0, 0, 0, 1, 2, 3, 4, 5, 6}; - const auto kmp = strings::kmp(word); + const auto kmp = string::kmp(word); EXPECT_EQ(kmp, expected); } @@ -27,7 +27,7 @@ TEST(KMP, NoPrefix) const std::string word = "abcdef"; const std::vector expected {0, 0, 0, 0, 0, 0}; - const auto kmp = strings::kmp(word); + const auto kmp = string::kmp(word); EXPECT_EQ(kmp, expected); } @@ -37,7 +37,7 @@ TEST(KMP, AllSame) const std::string word = "aaaaaa"; const std::vector expected {0, 1, 2, 3, 4, 5}; - const auto kmp = strings::kmp(word); + const auto kmp = string::kmp(word); EXPECT_EQ(kmp, expected); } @@ -47,7 +47,7 @@ TEST(KMP, Empty) const std::string word = ""; const std::vector expected {}; - const auto kmp = strings::kmp(word); + const auto kmp = string::kmp(word); EXPECT_EQ(kmp, expected); } diff --git a/competitive_programming/test/string/manacher.cpp b/competitive_programming/test/string/manacher.cpp new file mode 100644 index 0000000..7dd5766 --- /dev/null +++ b/competitive_programming/test/string/manacher.cpp @@ -0,0 +1,20 @@ +#include + +#include "string/manacher.hpp" + +TEST(Manacher, TestPalindromes) +{ + EXPECT_EQ(string::manacher("abcbcba"), 7); + EXPECT_EQ(string::manacher("mississippi"), 7); + EXPECT_EQ(string::manacher("ababacaca"), 5); + EXPECT_EQ(string::manacher("aaaaa"), 5); + EXPECT_EQ(string::manacher("aybabtu"), 3); + EXPECT_EQ(string::manacher("ihpohpzoffel"), 2); + EXPECT_EQ(string::manacher("flexflexvpqxierullgcfckjqflexflex"), 3); + EXPECT_EQ(string::manacher("obsession"), 3); + EXPECT_EQ(string::manacher("abcxcbaxcba"), 7); + EXPECT_EQ(string::manacher("aaccaabbaaccaaccaabbaaccaa"), 26); + EXPECT_EQ(string::manacher("a"), 1); + EXPECT_EQ(string::manacher("abb"), 2); + EXPECT_EQ(string::manacher("aaaaaaaaaa"), 10); +} \ No newline at end of file diff --git a/competitive_programming/test/string/z_function.cpp b/competitive_programming/test/string/z_function.cpp index ddfa7ca..bda890e 100644 --- a/competitive_programming/test/string/z_function.cpp +++ b/competitive_programming/test/string/z_function.cpp @@ -7,7 +7,7 @@ TEST(ZFunction, Palindrome) const std::string word = "abcbcba"; const std::vector expected {0, 0, 0, 0, 0, 0, 1}; - const auto z = strings::z_function(word); + const auto z = string::z_function(word); EXPECT_EQ(z, expected); } @@ -17,7 +17,7 @@ TEST(ZFunction, AllZeroes) const std::string word = "mississippi"; const std::vector expected {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - const auto z = strings::z_function(word); + const auto z = string::z_function(word); EXPECT_EQ(z, expected); } @@ -27,7 +27,7 @@ TEST(ZFunction, SomeEqualSuffixies) const std::string word = "ababacaca"; const std::vector expected {0, 0, 3, 0, 1, 0, 1, 0, 1}; - const auto z = strings::z_function(word); + const auto z = string::z_function(word); EXPECT_EQ(z, expected); } @@ -37,7 +37,7 @@ TEST(ZFunction, AllEqual) const std::string word = "aaaaa"; const std::vector expected {0, 4, 3, 2, 1}; - const auto z = strings::z_function(word); + const auto z = string::z_function(word); EXPECT_EQ(z, expected); }