Problem Statement: LOJ2303

Solution

Let $K$ be the maximum $k$ across all queries. For the full solution, $K = 50$.

We can solve this problem using brute force and hashing. When merging/separating queues, we enumerate all substrings covering the intersection of length less than or equal to $K$. We find the frequency of each substring using a hash table and maintain the queues using a doubly-linked list.

At first glance, we may think the time complexity is $\mathcal{O}(mL^2)$, because we add/remove at most $\mathcal{O}(K^2)$ substrings for each query. However, the actual number is smaller.

Looking back to the problem, we see another constraint $c \leq 10^3$, which means that the number of queries of type $2$ is at most $c$. Because the number of substrings at a given time is bounded by $\mathcal{O}(nK)$, the actual number of substrings we add/remove is $\mathcal{O}(nK + cK^2)$, because we remove at most $\mathcal{O}(cK^2)$ substrings. This runs in time, but with a large constant factor.

To avoid hashing collisions, consider using a $64$-bit modulus, with __int128_t for multiplication. To decrease the constant factor, use gp_hash_table instead of std::unordered_map. Then, you should get AC.

Implementation

/**
 * @author n685
 * @brief
 * @date 2024-07-31 15:14:32
 *
 *
 */
#include <bits/stdc++.h>

#ifdef LOCAL
#include "dd/debug.h"
#else
#define dbg(...) 42
#define dbgP(...) 420
#define dbgRP(...) 420420
void nline() {}
void bar() {}
#endif

template <class T> constexpr std::pair<T, T> exEucl(T a, T b) {
  if (a < b) {
    auto [x, y] = exEucl(b, a);
    return {y, x};
  }
  if (b == 0) {
    assert(a == 1);
    return {1, 0};
  }
  auto [x, y] = exEucl(b, a % b);
  return {y, x - (a / b) * y};
}

template <class Md, class V = int64_t>
// requires std::signed_integral<std::decay_t<decltype(Md::value)>>
struct Mod {
  using T = std::decay_t<decltype(Md::value)>;
  T val = 0;

  static constexpr T normalize(auto val) {
    using U = decltype(Md::value + val);
    U uval = static_cast<U>(val);
    U umd = static_cast<U>(Md::value);
    if (uval <= -umd || umd <= uval) {
      uval %= umd;
    }
    if (val < 0) {
      uval += umd;
    }
    return static_cast<T>(uval);
  }

  constexpr Mod() : val(0) {}
  constexpr explicit Mod(auto _val) : val(normalize(_val)) {}
  static inline const Mod ZERO = Mod(0);
  static inline const Mod ONE = Mod(1);
  static inline const Mod TWO = Mod(2);

  // addition
  constexpr Mod &operator+=(Mod b) {
    val += b.val;
    if (val >= Md::value) {
      val -= Md::value;
    }
    return *this;
  }
  friend constexpr Mod operator+(Mod a, Mod b) { return (a += b); }
  constexpr Mod &operator++() { return (*this += Mod(1)); }
  constexpr Mod operator++(int) {
    Mod res = *this;
    ++(*this);
    return res;
  }

  // subtraction
  constexpr Mod &operator-=(Mod b) {
    val -= b.val;
    if (val < 0) {
      val += Md::value;
    }
    return *this;
  }
  friend constexpr Mod operator-(Mod a, Mod b) { return (a -= b); }
  constexpr Mod &operator--() { return (*this -= Mod(1)); }
  constexpr Mod operator--(int) {
    Mod res = *this;
    --(*this);
    return res;
  }
  // negation
  constexpr Mod operator-() const { return Mod(-val); }

  // multiplication
  constexpr Mod &operator*=(Mod b) {
    // auto q = static_cast<long long>(static_cast<long double>(val) * b.val /
    //                                 Md::value);
    // val = normalize(val * b.val - q * Md::value);
    // return *this;
    val = static_cast<T>(static_cast<V>(val) * b.val % Md::value);
    return *this;
  }
  friend constexpr Mod operator*(Mod a, Mod b) { return (a *= b); }
  constexpr Mod binpow(std::integral auto b) const {
    Mod res = Mod(1), a = *this;
    while (b > 0) {
      if (b % 2 == 1) {
        res *= a;
      }
      a *= a;
      b /= 2;
    }
    return res;
  }

  // factorial
  // align with fft, if code fails to compile make this smaller (if using array)
  static constexpr int MXINV = 1 << 22;
  static inline bool init = false;
  static inline std::vector<Mod> ff, iff;
  static void resetFac() { init = false; }
  static void initFac() {
    if (init) {
      return;
    }
    ff.resize(MXINV + 1);
    ff[0] = Mod(1);
    for (int i = 1; i <= MXINV; ++i) {
      ff[i] = ff[i - 1] * Mod(i);
    }
    iff.resize(MXINV + 1);
    iff[MXINV] = ff[MXINV].largeInv();
    for (int i = MXINV - 1; i >= 0; --i) {
      iff[i] = iff[i + 1] * Mod(i + 1);
    }
    init = true;
  }
  static Mod fac(int v) {
    if (!init) {
      initFac();
    }
    return ff[v];
  }
  static Mod ifac(int v) {
    if (!init) {
      initFac();
    }
    return iff[v];
  }
  static Mod comb(int n, int k) {
    if (n < 0 || k < 0 || n < k) {
      return Mod(0);
    }
    return fac(n) * ifac(n - k) * ifac(k);
  }
  static Mod perm(int n, int k) {
    if (n < 0 || k < 0 || n < k) {
      return Mod(0);
    }
    return fac(n) * ifac(n - k);
  }

  // inverse
  Mod smallInv() const {
    return ifac(static_cast<int>(val)) * fac(static_cast<int>(val) - 1);
  }
  constexpr Mod largeInv() const {
    return Mod(exEucl(static_cast<V>(val), static_cast<V>(Md::value)).first);
    // return binpow(Md::value - 2);
  }
  Mod inv() const {
    if (val <= MXINV) {
      return smallInv();
    }
    return largeInv();
  }

  // sqrt
  std::optional<Mod> sqrt() {
    static std::mt19937 rng(
        std::chrono::steady_clock::now().time_since_epoch().count());
    Mod c = Mod::ZERO;
    while ((c * c - *this).binpow((Md::value - 1) / 2) == Mod::ONE) {
      c = Mod(rng());
    }
    if (c == Mod::ZERO) {
      return std::nullopt;
    }
    std::pair<Mod, Mod> res(Mod::ONE, Mod::ZERO), a(c, Mod::ONE);
    T b = (Md::value + 1) / 2;
    auto mul = [&c, this](const std::pair<Mod, Mod> &u,
                          const std::pair<Mod, Mod> &v) -> std::pair<Mod, Mod> {
      return {u.first * v.first + u.second * v.second * (c * c - *this),
              u.second * v.first + u.first * v.second};
    };
    while (b > 0) {
      if (b % 2 == 1) {
        res = mul(res, a);
      }
      a = mul(a, a);
      b /= 2;
    }
    return res.first;
    // return std::min(res.first, -res.first);
  }

  // comparison
  constexpr bool operator==(const Mod &b) const = default;
  constexpr std::strong_ordering operator<=>(const Mod &b) const = default;

  // io
  friend std::istream &operator>>(std::istream &in, Mod &a) {
    int64_t v;
    in >> v;
    a = Mod(v);
    return in;
  }
  friend std::ostream &operator<<(std::ostream &out, const Mod &a) {
    out << a.val;
    return out;
  }

  // conversion
  constexpr T value() const { return val; }
};

#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
const auto RANDOM =
    std::chrono::high_resolution_clock::now().time_since_epoch().count();
struct chash {
  static constexpr long double PI = std::numbers::pi_v<long double>;
  // any random-ish large odd number will do
  static inline const uint64_t C = static_cast<uint64_t>(2e18 * PI) + 71;
  // random 32-bit number
  static inline const uint32_t RANDOM = static_cast<uint32_t>(
      std::chrono::steady_clock::now().time_since_epoch().count());
  auto operator()(uint64_t x) const -> size_t {
    // see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
    return __builtin_bswap64((x ^ RANDOM) * C);
  }
};
template <std::integral T, class U> using Map = gp_hash_table<T, U, chash>;
// template <class T, class U> using Map = std::unordered_map<T, U>;

constexpr int64_t MOD = (int64_t{1} << 61) - 1;
using Mint =
    Mod<std::integral_constant<std::decay_t<decltype(MOD)>, MOD>, __int128_t>;
constexpr int MOD2 = 998244353;
using Mint2 = Mod<std::integral_constant<std::decay_t<decltype(MOD2)>, MOD2>>;

constexpr int MX = 50;

struct LL {
  Mint v;
  int l = -1, r = -1;
};

int main() {
#ifndef LOCAL
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
#endif

  std::mt19937_64 rng(
      std::chrono::steady_clock::now().time_since_epoch().count());
  const Mint p(rng());

  int n, m;
  std::cin >> n >> m;

  std::vector<Mint> ppow(n + 1), invp(n + 1);
  ppow[0] = Mint::ONE;
  invp[0] = Mint::ONE;
  for (int i = 1; i <= n; ++i) {
    ppow[i] = ppow[i - 1] * p;
    if (i == 1) {
      invp[i] = p.inv();
    } else {
      invp[i] = invp[i - 1] * invp[1];
    }
  }

  Map<int64_t, Mint2> freq;
  std::vector<LL> ll(n);
  for (int i = 0; i < n; ++i) {
    std::cin >> ll[i].v;
    ll[i].v += Mint('0');
    ++freq[ll[i].v.value()];
  }

  while ((m--) != 0) {
    int op;
    std::cin >> op;
    if (op == 1) {
      int i, j;
      std::cin >> i >> j;
      --i;
      --j;
      assert(ll[i].r == -1);
      assert(ll[j].l == -1);
      ll[i].r = j;
      ll[j].l = i;
      for (int k = 2; k <= MX; ++k) {
        int l = i, r = j;
        Mint hsh = ll[l].v + ppow[1] * ll[r].v;
        for (int t = 2; t < k; ++t) {
          if (ll[r].r == -1) {
            hsh *= ppow[1];
            l = ll[l].l;
            if (l == -1) {
              break;
            }
            hsh += ll[l].v;
          } else {
            r = ll[r].r;
            hsh += ppow[t] * ll[r].v;
          }
        }
        if (l == -1) {
          continue;
        }
        while (r != i) {
          ++freq[hsh.value()];
          hsh -= ppow[k - 1] * ll[r].v;
          l = ll[l].l;
          r = ll[r].l;
          if (l == -1) {
            break;
          }
          hsh *= ppow[1];
          hsh += ll[l].v;
        }
      }
    } else if (op == 2) {
      int i;
      std::cin >> i;
      --i;
      assert(ll[i].r != -1);
      assert(ll[ll[i].r].l != -1);
      for (int k = 2; k <= MX; ++k) {
        int l = i, r = i;
        Mint hsh = ll[l].v;
        for (int t = 1; t < k; ++t) {
          if (ll[r].r == -1) {
            hsh *= ppow[1];
            l = ll[l].l;
            if (l == -1) {
              break;
            }
            hsh += ll[l].v;
          } else {
            r = ll[r].r;
            hsh += ppow[t] * ll[r].v;
          }
        }
        if (l == -1) {
          continue;
        }
        while (r != i) {
          --freq[hsh.value()];
          hsh -= ppow[k - 1] * ll[r].v;
          l = ll[l].l;
          r = ll[r].l;
          if (l == -1) {
            break;
          }
          hsh *= ppow[1];
          hsh += ll[l].v;
        }
      }
      ll[ll[i].r].l = -1;
      ll[i].r = -1;
    } else {
      std::string s;
      int k;
      std::cin >> s >> k;
      if (k > n) {
        std::cout << "0\n";
        continue;
      }
      Mint2 ans(1);
      Mint hsh(0);
      for (int i = 0; i < k; ++i) {
        hsh += Mint(s[i]) * ppow[i];
      }
      ans *= freq[hsh.value()];
      for (int i = 1; i < std::ssize(s) - k + 1; ++i) {
        hsh -= Mint(s[i - 1]);
        hsh *= invp[1];
        hsh += Mint(s[i + k - 1]) * ppow[k - 1];
        ans *= freq[hsh.value()];
      }
      std::cout << ans << '\n';
    }
  }
}