diff --git a/algorithms/math/chinese_remainder_theorem.cpp b/algorithms/math/chinese_remainder_theorem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a28c5d27baf5f8e2dd20e88624eb6216869e041c
--- /dev/null
+++ b/algorithms/math/chinese_remainder_theorem.cpp
@@ -0,0 +1,47 @@
+/// Chinese Remainder Theorem
+///
+/// Description:
+///   Given $t$ linear congruences in the format: \[ x \equiv a_i \pmod{m_i} \]
+/// the Chinese Remainder Theorem (CRT) finds $x$ that satisfies every 
+/// given congruence or states that there are no solutions. \par
+///   This implementation does not require all $m_i$ to be coprime, it works
+/// with any value and returns the smallest possible $x$; infinitely many 
+/// solutions can be obtained by incrementing or decrementing 
+/// $LCM(m_1,m_2,...,m_t)$ from $x$.
+///
+/// Time: O(t log LCM(m_1,m_2,...,m_t))
+/// Space: O(t)
+///
+/// Caution:
+///   - It is very easy to get overflow, since the $LCM$ is computed from
+/// all $m_i$, BigInt or Python should be considered if inputs are too large
+
+ll norm(ll a, ll b) { return (a + b) % b; }
+
+pair<ll,ll> crt_single(ll a, ll n, ll b, ll m) {
+  ans_t e = ext_gcd(n, m);
+
+  if ((a - b) % e.d != 0)
+    return {-1,-1}; // No solution
+
+  ll ans = norm(a + e.x*(b-a) / e.d % (m/e.d)*n, n *m / e.d);
+  ll lcm = (n*m) / e.d;
+  return {norm(ans, lcm), lcm};
+}
+
+ll crt(vector<ll> a, vector<ll> m) {
+  ll res = a[0];
+  ll lcm = m[0];
+
+  int t = s.size();
+  for (int i = 1; i < t; ++i) {
+    auto ss = crt(res, lcm, a[i], m[i]);
+    if (ss.fi == -1)
+      return -1; // No solution
+
+    res = ss.fi;
+    lcm = ss.se;
+  }
+
+  return res;
+}
diff --git a/algorithms/math/extended_euclidean.cpp b/algorithms/math/extended_euclidean.cpp
index 85412b10371e71a97a65d4cdfd64f2e4f68e36a1..e9dbee732f946057308d037c861afc55fca7b283 100644
--- a/algorithms/math/extended_euclidean.cpp
+++ b/algorithms/math/extended_euclidean.cpp
@@ -3,17 +3,10 @@
 /// Time: O(log min(a,b))
 /// Space: O(1)
 
-int ext_gcd(int a, int b, int &x, int &y) {
-  if (a == 0) {
-    x = 0, y = 1;
-    return b;
-  }
+struct ans_t { ll x, y, d; };
 
-  int x1, y1;
-  int g = ext_gcd(b % a, a, x1, y1);
-
-  x = y1 - (b / a) * x1;
-  y = x1;
-
-  return g;
+ans_t ext_gcd(ll a, ll b) {
+  if (a == 0) return {0, 1, b};
+  ans_t e = ext_gcd(b % a, a);
+  return {e.y - (b/a)*e.x, e.x, e.d};
 }
diff --git a/algorithms/math/linear_diophantine_equation.cpp b/algorithms/math/linear_diophantine_equation.cpp
index c4a83747e8a13db763ecd62ad153edfdc758f4a2..085d7d7e13a185dd9a0c7194b5d9e9411b512084 100644
--- a/algorithms/math/linear_diophantine_equation.cpp
+++ b/algorithms/math/linear_diophantine_equation.cpp
@@ -24,15 +24,13 @@ struct Diophantine {
   { init(); }
 
   void init() {
-    int w0, z0;
-    d = ext_gcd(a, b, w0, z0);
+    ans_t e = ext_gcd(a, b); d = e.d;
     if (c % d == 0) {
-      x0 = w0 * (c / d);
-      y0 = z0 * (c / d);
+      x0 = e.x * (c / d);
+      y0 = e.y * (c / d);
       has_solution = true;
-    } else {
+    } else
       has_solution = false;
-    }
   }
 
   ii get(int t) {
diff --git a/algorithms/math/modular_multiplicative_inverse.cpp b/algorithms/math/modular_multiplicative_inverse.cpp
index 6b08aaef9e8e2133fb9993de32a5441d6137d3ab..a06e950382a7db8540c9d620f502159d7e87715e 100644
--- a/algorithms/math/modular_multiplicative_inverse.cpp
+++ b/algorithms/math/modular_multiplicative_inverse.cpp
@@ -11,7 +11,6 @@ int fermat(int a, int m) {
 // Extended Euclidean Algorithm: Used when m 
 // and a are coprime
 int extended_euclidean(int a, int m) {
-  int x, y;
-  int g = ext_gcd(a, m, x, y);
-  return (x % m + m) % m;
+  ans_t g = ext_gcd(a, m);
+  return (g.x % m + m) % m;
 }
diff --git a/caderno.pdf b/caderno.pdf
index 1392bc316a66f707dcffa27283608457fcb0c7ab..aa8baf52d7e241dfcb6f1668061ae0b42606296c 100644
Binary files a/caderno.pdf and b/caderno.pdf differ
diff --git a/contests/ICPC_LA18/F.cpp b/contests/ICPC_LA18/F.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..48d59be9b56b74fac067afb16a1e854b47170a42
--- /dev/null
+++ b/contests/ICPC_LA18/F.cpp
@@ -0,0 +1,484 @@
+#include <bits/stdc++.h>
+
+#define EPS 1e-6
+#define MOD 1000000007
+#define inf 0x3f3f3f3f
+#define llinf 0x3f3f3f3f3f3f3f3f
+
+#define fi first
+#define se second
+#define pb push_back
+#define ende '\n'
+
+#define all(x) (x).begin(), (x).end()
+#define rall(x) (x).rbegin(), (x).rend()
+#define mset(x, y) memset(&x, (y), sizeof(x))
+
+using namespace std; 
+
+using ll = long long;
+using ii = pair<ll,ll>;
+
+vector<ll> karatsuba(const vector<ll> &a, 
+    const vector<ll> &b) 
+{
+  int n = a.size();
+  vector<ll> res(n + n);
+
+  if (n <= 32) {
+    for (int i = 0; i < n; i++)
+      for (int j = 0; j < n; j++)
+        res[i + j] += a[i] * b[j];
+
+    return res;
+  }
+
+  int k = n >> 1;
+  vector<ll> a1(a.begin(), a.begin() + k);
+  vector<ll> a2(a.begin() + k, a.end());
+  vector<ll> b1(b.begin(), b.begin() + k);
+  vector<ll> b2(b.begin() + k, b.end());
+
+  vector<ll> a1b1 = karatsuba(a1, b1);
+  vector<ll> a2b2 = karatsuba(a2, b2);
+
+  for (int i = 0; i < k; i++)
+    a2[i] += a1[i];
+  for (int i = 0; i < k; i++)
+    b2[i] += b1[i];
+
+  vector<ll> r = karatsuba(a2, b2);
+  for (int i = 0; i < a1b1.size(); i++) 
+    r[i] -= a1b1[i];
+  for (int i = 0; i < a2b2.size(); i++) 
+    r[i] -= a2b2[i];
+
+  for (int i = 0; i < r.size(); i++) 
+    res[i + k] += r[i];
+  for (int i = 0; i < a1b1.size(); i++) 
+    res[i] += a1b1[i];
+  for (int i = 0; i < a2b2.size(); i++) 
+    res[i + n] += a2b2[i];
+
+  return res;
+}
+
+const int base = 1000000000;
+const int base_d = 9;
+
+struct BigInt {
+  int sign;
+  vector<int> num;
+
+  BigInt() : sign(1) {}
+  BigInt(ll x) { *this = x; }
+
+  void operator=(const BigInt &x) {
+    sign = x.sign;
+    num = x.num;
+  }
+
+  void operator=(ll x) {
+    sign = 1;
+    if (x < 0) sign = -1, x = -x;
+    for (; x > 0; x /= base)
+      pb(x % base);
+  }
+
+  BigInt operator+(const BigInt &x) const {
+    if (sign != x.sign) return *this - (-x);
+
+    int carry = 0;
+    BigInt res = x;
+
+    for (int i = 0; i < max(size(), x.size()) || carry; ++i) {
+      if (i == (int) res.size())
+        res.push_back(0);
+
+      res[i] += carry + (i < size() ? num[i] : 0);
+      carry = res[i] >= base;
+      if (carry) res[i] -= base;
+    }
+
+    return res;
+  }
+
+  BigInt operator-(const BigInt &x) const {
+    if (sign != x.sign) return *this + (-x);
+    if (abs() < x.abs()) return -(x - *this);
+
+    int carry = 0;
+    BigInt res = *this;
+
+    for (int i = 0; i < x.size() || carry; ++i) {
+      res[i] -= carry + (i < x.size() ? x[i] : 0);
+      carry = res[i] < 0;
+      if (carry) res[i] += base;
+    }
+
+    res.trim();
+    return res;
+  }
+
+  void operator*=(int x) {
+    if (x < 0) sign = -sign, x = -x;
+
+    int carry = 0;
+    for (int i = 0; i < size() || carry; ++i) {
+      if (i == size()) pb(0);
+      ll cur = num[i] * (ll) x + carry;
+
+      carry  = (int) (cur / base);
+      num[i] = (int) (cur % base);
+    }
+
+    trim();
+  }
+
+  BigInt operator*(int x) const {
+    BigInt res = *this;
+    res *= x;
+    return res;
+  }
+
+  friend pair<BigInt, BigInt> divmod(const BigInt &a1,
+      const BigInt &b1)
+  {
+    int norm = base / (b1.back() + 1);
+    BigInt a = a1.abs() * norm;
+    BigInt b = b1.abs() * norm;
+    BigInt q, r;
+    q.resize(a.size());
+
+    for (int i = a.size() - 1; i >= 0; i--) {
+      r *= base;
+      r += a[i];
+
+      int s1 = r.size() <= b.size() ? 0 : r[b.size()];
+      int s2 = r.size() <= b.size() - 1 ? 0 : r[b.size() - 1];
+      int d = ((ll) base * s1 + s2) / b.back();
+
+      r -= b * d;
+      while (r < 0) r += b, --d;
+      q[i] = d;
+    }
+
+    q.sign = a1.sign * b1.sign;
+    r.sign = a1.sign;
+    q.trim(); r.trim();
+
+    return make_pair(q, r / norm);
+  }
+
+  BigInt operator/(const BigInt &x) const {
+    return divmod(*this, x).fi;
+  }
+
+  BigInt operator%(const BigInt &x) const {
+    return divmod(*this, x).se;
+  }
+
+  void operator/=(int x) {
+    if (x < 0) sign = -sign, x = -x;
+
+    for (int i = size() - 1, rem = 0; i >= 0; --i) {
+      ll cur = num[i] + rem * (ll) base;
+      num[i] = (int) (cur / x);
+      rem = (int) (cur % x);
+    }
+
+    trim();
+  }
+
+  BigInt operator/(int x) const {
+    BigInt res = *this;
+    res /= x;
+    return res;
+  }
+
+  int operator%(int x) const {
+    if (x < 0) x = -x;
+
+    int m = 0;
+    for (int i = size() - 1; i >= 0; --i)
+      m = (num[i] + m * (ll) base) % x;
+
+    return m * sign;
+  }
+
+  void operator+=(const BigInt &x) { *this = *this + x; }
+  void operator-=(const BigInt &x) { *this = *this - x; }
+  void operator*=(const BigInt &x) { *this = *this * x; }
+  void operator/=(const BigInt &x) { *this = *this / x; }
+
+  bool operator<(const BigInt &x) const {
+    if (sign != x.sign)
+      return sign < x.sign;
+
+    if (size() != x.size())
+      return size() * sign < x.size() * x.sign;
+
+    for (int i = size() - 1; i >= 0; i--)
+      if (num[i] != x[i])
+        return num[i] * sign < x[i] * sign;
+
+    return false;
+  }
+
+  bool operator>(const BigInt &x) const {
+    return x < *this;
+  }
+  bool operator<=(const BigInt &x) const {
+    return !(x < *this);
+  }
+  bool operator>=(const BigInt &x) const {
+    return !(*this < x);
+  }
+  bool operator==(const BigInt &x) const {
+    return !(*this < x) && !(x < *this);
+  }
+  bool operator!=(const BigInt &x) const {
+    return *this < x || x < *this;
+  }
+
+  void trim() {
+    while (!empty() && !back()) pop_back();
+    if (empty()) sign = 1;
+  }
+
+  bool is_zero() const {
+    return empty() || (size() == 1 && !num[0]);
+  }
+
+  BigInt operator-() const {
+    BigInt res = *this;
+    res.sign = -sign;
+    return res;
+  }
+
+  BigInt abs() const {
+    BigInt res = *this;
+    res.sign *= res.sign;
+    return res;
+  }
+
+  ll to_long() const {
+    ll res = 0;
+    for (int i = size() - 1; i >= 0; i--)
+      res = res * base + num[i];
+    return res * sign;
+  }
+
+  void read(const string &s) {
+    sign = 1;
+    num.clear();
+
+    int pos = 0;
+    while (pos < s.size() &&
+        (s[pos] == '-' || s[pos] == '+')) {
+      if (s[pos] == '-')
+        sign = -sign;
+      ++pos;
+    }
+
+    for (int i = s.size() - 1; i >= pos; i -= base_d) {
+      int x = 0;
+      for (int j = max(pos, i - base_d + 1); j <= i; j++)
+        x = x * 10 + s[j] - '0';
+      num.push_back(x);
+    }
+
+    trim();
+  }
+
+  friend istream& operator>>(istream &stream, BigInt &v) {
+    string s; stream >> s;
+    v.read(s);
+    return stream;
+  }
+
+  friend ostream& operator<<(ostream &stream, const BigInt &x) {
+    if (x.sign == -1)
+      stream << '-';
+
+    stream << (x.empty() ? 0 : x.back());
+    for (int i = x.size() - 2; i >= 0; --i)
+      stream << setw(base_d) << setfill('0') << x.num[i];
+
+    return stream;
+  }
+
+  static vector<int> convert_base(
+      const vector<int> &a,
+      int oldd, int newd) {
+    vector<ll> p(max(oldd, newd) + 1);
+    p[0] = 1;
+    for (int i = 1; i < p.size(); i++)
+      p[i] = p[i - 1] * 10;
+
+    ll cur = 0;
+    int curd = 0;
+    vector<int> res;
+
+    for (int i = 0; i < a.size(); i++) {
+      cur += a[i] * p[curd];
+      curd += oldd;
+
+      while (curd >= newd) {
+        res.pb(int(cur % p[newd]));
+        cur /= p[newd];
+        curd -= newd;
+      }
+    }
+
+    res.pb((int) cur);
+    while (!res.empty() && !res.back())
+      res.pop_back();
+    return res;
+  }
+
+  BigInt operator*(const BigInt &x) const {
+    vector<int> a6 = convert_base(this->num, base_d, 6);
+    vector<int> b6 = convert_base(x.num, base_d, 6);
+
+    vector<ll> a(all(a6));
+    vector<ll> b(all(b6));
+
+    while (a.size() < b.size()) a.pb(0);
+    while (b.size() < a.size()) b.pb(0);
+    while (a.size() & (a.size() - 1))
+      a.pb(0), b.pb(0);
+
+    vector<ll> c = karatsuba(a, b);
+
+    BigInt res;
+    int carry = 0;
+    res.sign = sign * x.sign;
+
+    for (int i = 0; i < c.size(); i++) {
+      ll cur = c[i] + carry;
+      res.pb((int) (cur % 1000000));
+      carry = (int) (cur / 1000000);
+    }
+
+    res.num = convert_base(res.num, 6, base_d);
+    res.trim();
+    return res;
+  }
+
+  // Handles vector operations.
+  int back() const { return num.back(); }
+  bool empty() const { return num.empty(); }
+  size_t size() const { return num.size(); }
+
+  void pop_back() { num.pop_back(); }
+  void resize(int x) { num.resize(x); }
+  void push_back(int x) { num.push_back(x); }
+
+  int &operator[](int i) { return num[i]; }
+  int operator[](int i) const { return num[i]; }
+};
+
+using pb = pair<BigInt,BigInt>;
+
+template <typename T> 
+struct ans_t { T x, y, d; };
+
+template <typename T>
+ans_t<T> ext_gcd(T a, T b) {
+  if (a == 0) return {0, 1, b};
+  ans_t<T> e = ext_gcd(b % a, a);
+  return {e.y - (b/a) * e.x, e.x, e.d};
+}
+
+template <typename T>
+T norm(T a, T b) {
+  return (a + b) % b;
+}
+
+template <typename T>
+pb crt(T a, T n, T b, T m) {
+  ans_t<T> e = ext_gcd(n, m);
+
+  if ((a - b) % e.d != 0)
+    return {-1,-1};
+
+  T ans = norm(a + e.x*(b-a) / e.d % (m/e.d)*n, n *m / e.d);
+  T lcm = (n*m) / e.d;
+  return {norm(ans, lcm), lcm};
+}
+
+int main() {
+  ios::sync_with_stdio(0);
+  cin.tie(0);
+
+  int b, z; cin >> b >> z;
+  vector<vector<int>> mat(b, vector<int>(z+1));
+  for (int i = 0; i < b; ++i)
+    for (int j = 0; j <= z; ++j)
+      cin >> mat[i][j];
+
+  vector<vector<int>> time(z+1, vector<int>(501));
+  for (int i = 0; i < b; ++i) {
+    int curr = mat[i][0];
+    time[curr][0] |= (1 << i);
+    for (int t = 1; t <= 500; ++t) {
+      curr = mat[i][curr]; 
+      time[curr][t] |= (1 << i);
+    }
+  }
+
+  vector<vector<int>> a(z + 1, vector<int>(b, -1));
+  vector<vector<int>> m(z + 1, vector<int>(b, -1));
+  for (int i = 1; i <= z; ++i) {
+    for (int t = 0; t <= 500; ++t) {
+      if (time[i][t] == (1 << b) - 1)
+        return cout << i << " " << t << ende, 0;
+
+      for (int j = 0; j < b; ++j) {
+        if (time[i][t] & (1 << j)) {
+          if (a[i][j] == -1)
+            a[i][j] = t;
+          else if (m[i][j] == -1)
+            m[i][j] = t - a[i][j];
+        }
+      }
+    }
+  }
+
+  int zoo = 0;
+  BigInt ans = llinf*2;
+  for (int i = 1; i <= z; ++i) {
+    bool poss = true;
+    for (int j = 0; j < b; ++j)
+      if (a[i][j] == -1 || m[i][j] == -1) {
+        poss = false;
+        break;
+      }
+
+    if (!poss) continue;
+
+    BigInt res = a[i][0];
+    BigInt lcm = m[i][0];
+    for (int j = 1; j < b; ++j) {
+      pb ss = crt(res, lcm, BigInt(a[i][j]), BigInt(m[i][j]));
+      if (ss.fi == -1) {
+        poss = false;
+        break;
+      }
+
+      res = ss.fi;
+      lcm = ss.se;
+    }
+
+    if (!poss) continue;
+    else {
+      if (ans > res)
+        zoo = i, ans = res;
+    }
+  }
+
+  if (ans == llinf*2) cout << "*" << ende;
+  else cout << zoo << " " << ans << ende;
+  return 0;
+}