Hướng dẫn giải của Bedao Grand Contest 17 - Hệ số nhị thức


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Subtask 1: ~r_t \leq 10^3~

Ta chuẩn bị trước ~F(n)~ ~\forall n \le 10^3~. Với mỗi ~n~, ta duyệt ~k~ từ ~0~ đến ~n~. Vấn đề còn lại là tính ~\binom{n}{k} \bmod 2~.

Ta có ~\binom{n}{k} = \frac{n!}{k!(n-k)!}~. Dễ dàng tính được số lượng thừa số ~2~ trong ~n!~ bằng cách duyệt ~i~ từ ~1~ đến ~n~ và cộng vào số lượng thừa số ~2~ trong phân tích thừa số nguyên tố của ~i~, từ đó dễ dàng xác định được tính chẵn lẻ của ~\binom{n}{k}~.

Sau khi tính được ~F(n)~ ~\forall n \le 10^3~, ta dùng tổng cộng dồn để truy vấn tổng ~F(n)~ trong đoạn trong ~O(1)~

Độ phức tạp: ~O(10^6 +q)~

Subtask 2: ~r_t \leq 10^5~

Theo định lý Lucas, ~\binom{n}{r} \bmod p \equiv \prod_{i=0}^{k} \binom{n_i}{r_i} \bmod p~ với ~p~ nguyên tố.

Với ~p=2~ ta có ~n_k n_{k-1} \ldots n_0~ là biểu diễn nhị phân của ~n~, ~r_k r_{k-1} \ldots r_0~ là biểu diễn nhị phân của ~r~ (~0 \leq n_i, r_i \leq 1 \ \forall i \in [0,k]~)

~\binom{n}{r}~ lẻ khi và chỉ khi ~\binom{n_i}{r_i}~ lẻ ~\forall i \in [0,k]~. Suy ra ~r_i \leq n_i \ \forall i \in [0, k]~ (do ~\binom{1}{0} = \binom{1}{1}=1~). Vậy ~n_k n_{k-1} \ldots n_0~ là supermask của ~r_k r_{k-1} \ldots r_0~. Từ đó suy ra số lượng ~r~ tương ứng với mỗi ~n~ là ~2^{\text{popcount}(n)}~, với ~\text{popcount}(n)~ là số lượng bit ~1~ khi biểu diễn ~n~ dưới dạng hệ nhị phân (mỗi bit ~0~ của ~n~ chỉ tương ứng với một cách điền duy nhất trong ~r~, còn bit ~1~ của ~n~ thì ta có thể điền vào vị trí tương ứng của ~r~ là ~0/1~).

Biết được số lượng ~r~ sao cho ~\binom{n}{r}~ lẻ, ta dễ dàng suy ra được số lượng ~r~ sao cho ~\binom{n}{r}~ chẵn. Với ~n \leq 10^5~, ta cũng có thể chuẩn bị trước mảng ~F(n) = \sum_{i=1}^{n} 2^{\text{popcount(i)}}~, từ đó suy ra đáp án cho mỗi truy vấn là ~F(r)-F(l-1)~

Độ phức tạp: ~O(10^5+q)~

Subtask 3: Giới hạn gốc

Ta sử dụng quy hoạch động chữ số để tối ưu subtask 2. Gọi ~\text{dp}(i, \text{smaller}, \text{greater})~ là đáp án bài toán khi:

  • xét đến bit thứ ~i~

  • số đang xét đã chắc chắn nhỏ hơn cận trên chưa

  • số đang xét đã chắc chắn lớn hơn cận dưới chưa

Với mỗi bit thứ ~i~, ta duyệt mọi cách chọn bit ~b~ cho vị trí này, giả sử trạng thái tiếp theo là ~\text{dp}(i-1,s',g')~

  • Nếu ~b=0~, thì chắc chắn bit thứ ~i~ của ~r~ phải là ~0~ nên ta chỉ có một cách chọn bit cho vị trí thứ ~i~ là ~0~. Chuyển sang trạng thái ~\text{dp}(i-1,s',g')~

  • Nếu ~b=1~, thì bit thứ ~i~ của ~r~ có thể là ~0~ hoặc ~1~. Chuyển sang trạng thái ~2 \times \text{dp}(i-1,s',g')~

  • Kết quả cho ~\text{dp}(i, \ldots)~ là hợp của hai trạng thái trên

Với mỗi lần tính toán ta tốn ~O(60 \cdot 2 \cdot 2)~ ~\sim~ ~O(240)~. Tuy nhiên ta có thể tối ưu để chỉ tốn ~O(60)~ bằng cách chỉ cache các trạng thái ~\text{dp}(i, 1, 1)~, từ đó ta chỉ cần ~\texttt{memset}~ mảng ~\text{dp}~ một lần duy nhất trong suốt chương trình.

Độ phức tạp: ~O(q \cdot 60)~ hoặc ~O(q \cdot 240)~

#include <bits/stdc++.h>  // QioCas

using namespace std;
using ll = long long;

template <typename T>
T inverse(T a, T m) {
  T u = 0, v = 1;
  while (a != 0) {
    T t = m / a;
    m -= t * a;
    swap(a, m);
    u -= t * v;
    swap(u, v);
  }
  assert(m == 1);
  return u;
}

template <typename T>
class Modular {
 public:
  using Type = typename decay<decltype(T::value)>::type;

  constexpr Modular() : value() {}
  template <typename U>
  Modular(const U& x) {
    value = normalize(x);
  }

  template <typename U>
  static Type normalize(const U& x) {
    Type v;
    if (-mod() <= x && x < mod())
      v = static_cast<Type>(x);
    else
      v = static_cast<Type>(x % mod());
    if (v < 0) v += mod();
    return v;
  }

  const Type& operator()() const { return value; }
  template <typename U>
  explicit operator U() const { return static_cast<U>(value); }
  constexpr static Type mod() { return T::value; }

  Modular& operator+=(const Modular& other) {
    if ((value += other.value) >= mod()) value -= mod();
    return *this;
  }
  Modular& operator-=(const Modular& other) {
    if ((value -= other.value) < 0) value += mod();
    return *this;
  }
  template <typename U>
  Modular& operator+=(const U& other) { return *this += Modular(other); }
  template <typename U>
  Modular& operator-=(const U& other) { return *this -= Modular(other); }
  Modular& operator++() { return *this += 1; }
  Modular& operator--() { return *this -= 1; }
  Modular operator++(int) {
    Modular result(*this);
    *this += 1;
    return result;
  }
  Modular operator--(int) {
    Modular result(*this);
    *this -= 1;
    return result;
  }
  Modular operator-() const { return Modular(-value); }

  template <typename U = T>
  typename enable_if<is_same<typename Modular<U>::Type, int>::value, Modular>::type& operator*=(const Modular& rhs) {
#ifdef _WIN32
    uint64_t x = static_cast<int64_t>(value) * static_cast<int64_t>(rhs.value);
    uint32_t xh = static_cast<uint32_t>(x >> 32), xl = static_cast<uint32_t>(x), d, m;
    asm(
        "divl %4; \n\t"
        : "=a"(d), "=d"(m)
        : "d"(xh), "a"(xl), "r"(mod()));
    value = m;
#else
    value = normalize(static_cast<int64_t>(value) * static_cast<int64_t>(rhs.value));
#endif
    return *this;
  }
  template <typename U = T>
  typename enable_if<is_same<typename Modular<U>::Type, long long>::value, Modular>::type& operator*=(const Modular& rhs) {
    long long q = static_cast<long long>(static_cast<long double>(value) * rhs.value / mod());
    value = normalize(value * rhs.value - q * mod());
    return *this;
  }
  template <typename U = T>
  typename enable_if<!is_integral<typename Modular<U>::Type>::value, Modular>::type& operator*=(const Modular& rhs) {
    value = normalize(value * rhs.value);
    return *this;
  }

  Modular& operator/=(const Modular& other) { return *this *= Modular(inverse(other.value, mod())); }

  friend const Type& abs(const Modular& x) { return x.value; }

  template <typename U>
  friend bool operator==(const Modular<U>& lhs, const Modular<U>& rhs);

  template <typename U>
  friend bool operator<(const Modular<U>& lhs, const Modular<U>& rhs);

  template <typename V, typename U>
  friend V& operator>>(V& stream, Modular<U>& number);

 private:
  Type value;
};

template <typename T>
bool operator==(const Modular<T>& lhs, const Modular<T>& rhs) { return lhs.value == rhs.value; }
template <typename T, typename U>
bool operator==(const Modular<T>& lhs, U rhs) { return lhs == Modular<T>(rhs); }
template <typename T, typename U>
bool operator==(U lhs, const Modular<T>& rhs) { return Modular<T>(lhs) == rhs; }

template <typename T>
bool operator!=(const Modular<T>& lhs, const Modular<T>& rhs) { return !(lhs == rhs); }
template <typename T, typename U>
bool operator!=(const Modular<T>& lhs, U rhs) { return !(lhs == rhs); }
template <typename T, typename U>
bool operator!=(U lhs, const Modular<T>& rhs) { return !(lhs == rhs); }

template <typename T>
bool operator<(const Modular<T>& lhs, const Modular<T>& rhs) { return lhs.value < rhs.value; }

template <typename T>
Modular<T> operator+(const Modular<T>& lhs, const Modular<T>& rhs) { return Modular<T>(lhs) += rhs; }
template <typename T, typename U>
Modular<T> operator+(const Modular<T>& lhs, U rhs) { return Modular<T>(lhs) += rhs; }
template <typename T, typename U>
Modular<T> operator+(U lhs, const Modular<T>& rhs) { return Modular<T>(lhs) += rhs; }

template <typename T>
Modular<T> operator-(const Modular<T>& lhs, const Modular<T>& rhs) { return Modular<T>(lhs) -= rhs; }
template <typename T, typename U>
Modular<T> operator-(const Modular<T>& lhs, U rhs) { return Modular<T>(lhs) -= rhs; }
template <typename T, typename U>
Modular<T> operator-(U lhs, const Modular<T>& rhs) { return Modular<T>(lhs) -= rhs; }

template <typename T>
Modular<T> operator*(const Modular<T>& lhs, const Modular<T>& rhs) { return Modular<T>(lhs) *= rhs; }
template <typename T, typename U>
Modular<T> operator*(const Modular<T>& lhs, U rhs) { return Modular<T>(lhs) *= rhs; }
template <typename T, typename U>
Modular<T> operator*(U lhs, const Modular<T>& rhs) { return Modular<T>(lhs) *= rhs; }

template <typename T>
Modular<T> operator/(const Modular<T>& lhs, const Modular<T>& rhs) { return Modular<T>(lhs) /= rhs; }
template <typename T, typename U>
Modular<T> operator/(const Modular<T>& lhs, U rhs) { return Modular<T>(lhs) /= rhs; }
template <typename T, typename U>
Modular<T> operator/(U lhs, const Modular<T>& rhs) { return Modular<T>(lhs) /= rhs; }

template <typename T, typename U>
Modular<T> power(const Modular<T>& a, const U& b) {
  assert(b >= 0);
  Modular<T> x = a, res = 1;
  U p = b;
  while (p > 0) {
    if (p & 1) res *= x;
    x *= x;
    p >>= 1;
  }
  return res;
}

template <typename T>
bool IsZero(const Modular<T>& number) {
  return number() == 0;
}

template <typename T>
string to_string(const Modular<T>& number) {
  return to_string(number());
}

// U == std::ostream? but done this way because of fastoutput
template <typename U, typename T>
U& operator<<(U& stream, const Modular<T>& number) {
  return stream << number();
}

// U == std::istream? but done this way because of fastinput
template <typename U, typename T>
U& operator>>(U& stream, Modular<T>& number) {
  typename common_type<typename Modular<T>::Type, long long>::type x;
  stream >> x;
  number.value = Modular<T>::normalize(x);
  return stream;
}

/*
using ModType = int;

struct VarMod { static ModType value; };
ModType VarMod::value;
ModType& md = VarMod::value;
using Mint = Modular<VarMod>;
*/

constexpr int md = (int)1e9 + 7;
using Mint = Modular<std::integral_constant<decay<decltype(md)>::type, md>>;

/*vector<Mint> fact(1, 1);
vector<Mint> inv_fact(1, 1);

Mint C(int n, int k) {
  if (k < 0 || k > n) {
    return 0;
  }
  while ((int) fact.size() < n + 1) {
    fact.push_back(fact.back() * (int) fact.size());
    inv_fact.push_back(1 / fact.back());
  }
  return fact[n] * inv_fact[k] * inv_fact[n - k];
}*/

#define MASK(k) (1LL << (k))
#define BIT(x, k) ((x) >> (k) & 1)

int64_t l, r;
int cnt[60][2][2], timer = 0;
Mint dp[60][2][2];
Mint div2;

Mint dfs(int i = 59, bool OK1 = false, bool OK2 = false) {
  if (i == -1) return 1;
  if (exchange(cnt[i][OK1][OK2], timer) == timer) return dp[i][OK1][OK2];
  Mint& ans = dp[i][OK1][OK2];
  ans = 0;
  for (int w = 0; w < 2; ++w) {
    if (!OK1 && w < BIT(l, i)) continue;
    if (!OK2 && BIT(r, i) < w) continue;
    ans += MASK(w) * dfs(i - 1, OK1 || (BIT(l, i) < w), OK2 || (w < BIT(r, i)));
  }
  return ans;
}

Mint sum(int64_t L, int64_t R) {
  return div2 * R * (R + 1) - div2 * L * (L - 1);
}

int main() {
  cin.tie(NULL)->sync_with_stdio(false);

  div2 = Mint(1) / 2;

  int q, phi;
  cin >> q >> phi;

  for (int i = 1; i <= q; ++i) {
    cin >> l >> r;
    ++timer;
    Mint ans = dfs();
    if (!phi) ans = sum(1 + l, 1 + r) - ans;
    cout << ans << '\n';
  }
  return 0;
}

Bình luận

Hãy đọc nội quy trước khi bình luận.


Không có bình luận tại thời điểm này.