1. 程式人生 > >BZOJ3601. 一個人的數論(高斯消元+狄利克雷卷積)及關於「前 $n$ 個正整數的 $k$ 次冪之和是 $k+1$ 次多項式」的證明

BZOJ3601. 一個人的數論(高斯消元+狄利克雷卷積)及關於「前 $n$ 個正整數的 $k$ 次冪之和是 $k+1$ 次多項式」的證明

題目連結

https://www.lydsy.com/JudgeOnline/problem.php?id=3601

題解

首先還是基本的推式子:

\[\begin{aligned}f_d(n) &= \sum_{i = 1}^n [{\rm gcd}(i, n) = 1]i^d \\ &= \sum_{i = 1}^n i^d \sum_{k | i, k | n}\mu(k) \\ &= \sum_{k | n} \mu(k) \sum_{k | i} i^d \\ &= \sum_{k |n} \mu(k)k^d \sum_{i = 1}^{\frac{n}{k}}i^d\end{aligned}\]

\(g(x) = \sum_\limits{i = 1}^x i^d\),那麼原式即為 $ \sum_\limits{k |n} \mu(k)k^d g(\frac{n}{k})$。

關於函式 \(g(x)\),這其實是一個 \(d + 1\) 次多項式。在這裡我們作簡要證明。也就是我們要證明「前 \(n\) 個正整數的 \(k\) 次冪之和是 \(k+1\) 次多項式」。這裡把我之前的一篇部落格中的內容放出來。證明如下:

\(S(n, k) = \sum_{i = 1}^{n} i^k\),那麼我們的目的無非是要證明 \(S(n, k)\) 與一個關於 \(n\)\(k + 1\)

次多項式存在某種等式關係。我們作如下考慮:

  • 我們將兩個 \(k + 1\) 次多項式 \((n + 1)^{k + 1}\)\(n^{k + 1}\) 相減,得到: \[\begin{aligned}(n + 1)^{k + 1}-n^{k + 1} &= \left(\sum_{i = 0}^{k + 1}\left({\begin{matrix}k + 1 \\ i\end{matrix}}\right)n^i\right) - n^{k + 1} \\ &= \sum_{i = 0}^k\left({\begin{matrix}k + 1 \\ i\end{matrix}}\right)n^i \end{aligned}\]
    其中,\((n + 1)^{k + 1} = \sum_{i = 0}^{k + 1}\left(^{k + 1} _ {\ \ \ i\ \ \ }\right)n^i\) 用到了二項式定理。
  • 多項式 \(n^{k + 1}\)\((n - 1)^{k + 1}\) 相減,得到: \[n^{k + 1} - (n - 1)^{k + 1} = \sum_{i = 0}^k \left({\begin{matrix}k + 1 \\ i\end{matrix}}\right)(n - 1)^i\]
  • \(\cdots\)
  • 多項式 \(1^{k + 1}\)\(0^{k + 1}\) 相減,得到 \[1^{k + 1} - 0^{k + 1} = \sum_{i = 0}^k\left({\begin{matrix}k + 1 \\ i\end{matrix}}\right)0^i\]
  • 將上面所有式子相加,得到 \[(n + 1)^{k + 1} = \sum_{i = 0}^{k}\left({\begin{matrix}k + 1 \\ i\end{matrix}}\right)S(n, i)\]

顯然,通過該式子,我們可以遞迴證明上述結論,即:當 \(k = 0\) 時,\(S(n, 0)\) 顯然是一個 \(1\) 次多項式;當 \(k = 1\) 時,可以通過將該式子移項得到 \(S(n, 1)\) 是一個 \(2\) 次多項式;當 \(k = 2\) 時同理可得 \(S(n, 2)\) 是一個 \(3\) 次多項式……自然 \(S(n, k)\) 就是一個 \(k + 1\) 次多項式。

既然 \(g(x)\) 已經是一個 \(d + 1\) 次多項式,那麼我們就可以將 \(g(x)\) 寫成普通的多項式形式,即:\(g(x) = \sum_\limits{i = 0}^{d + 1}a_{i}x^i\)。由於 \(d \leq 100\),因此每一項的係數 \(a_i\) 可以通過高斯消元求得,複雜度是可接受的。我們將 \(g(x)\) 代入原答案式,得到:

\[\begin{aligned} f_d(n) &= \sum_\limits{k | n} \mu(k)k^d \sum_\limits{i = 0}^{d + 1}a_{i}\left(\frac{n}{k}\right)^i \\ &= \sum_{i = 0}^{d + 1}a_i \sum_{k | n} \mu(k) k^d \left(\frac{n}{k}\right)^i\end{aligned}\]

\(h_i(x) = \sum_\limits{k | x} \mu(k) k^d \left(\frac{x}{k}\right)^i\),顯然,\(h_i\) 是兩個形如 \(\mu(x)x^p\)\(x^p\) 的積性函式的狄利克雷卷積。因此 \(h_i\) 本身也是一個積性函式,由於 \(n\) 的唯一分解式為 \(n = \prod_\limits{k = 1}^{w} p_k ^{\alpha_k}\),故有 \(h_i(n) = \prod_\limits{k = 1}^w h(p_k^{\alpha_k})\)

考慮如何求單個 \(h_i(p ^{\alpha})\)

\[\begin{aligned} h_i(p ^{\alpha}) &= \sum_{j = 0}^{\alpha} \mu(p^j)p^{jd} p^{(\alpha - j)i}\end{aligned}\]

由於當 \(j = 0\) 時,\(\mu(p^j) = 1\);當 \(j = 1\) 時,\(\mu(p^j) = -1\);當 \(j > 1\) 時,\(\mu(p^j) = 0\)。故有:

\[\begin{aligned} h_i(p ^{\alpha}) &= p^{\alpha i} - p^{\alpha i - i + d}\end{aligned}\]

這樣,單個 \(h_i(p ^ {\alpha})\) 就能用快速冪在 \(O(\log \alpha)\) 的時間內求出。

解決整個問題的時間複雜度為 \(O(d^3 + dw \log \alpha)\)

程式碼

#include<bits/stdc++.h>

using namespace std;

const int N = 1e3 + 10, mod = 1e9 + 7;

void add(int& x, int y) {
  x += y;
  if (x >= mod) {
    x -= mod;
  }
}

void sub(int& x, int y) {
  x -= y;
  if (x < 0) {
    x += mod;
  }
}

int mul(int x, int y) {
  return (long long) x * y % mod;
}

int qpow(int v, int p) {
  int result = 1;
  for (; p; p >>= 1, v = mul(v, v)) {
    if (p & 1) {
      result = mul(result, v);
    }
  }
  return result;
}

int d, w, p[N], alpha[N], a[N][N];

void get_coefficient() {
  int sum = 0;
  for (int i = 0; i <= d + 1; ++i) {
    add(sum, qpow(i, d));
    a[i][d + 2] = sum;
    int pow_value = 1;
    for (int j = 0; j <= d + 1; ++j) {
      a[i][j] = pow_value;
      pow_value = mul(pow_value, i);
    }
  }
  for (int i = 0; i <= d + 1; ++i) {
    int rev = i;
    for (int j = i + 1; j <= d + 1; ++j) {
      if (a[j][i]) {
        rev = j;
        break;
      }
    }
    if (rev != i) {
      for (int j = i; j <= d + 2; ++j) {
        swap(a[rev][j], a[i][j]);
      }
    }
    for (int j = i + 1; j <= d + 1; ++j) {
      int p = mul(a[j][i], qpow(a[i][i], mod - 2));
      for (int k = i; k <= d + 2; ++k) {
        sub(a[j][k], mul(a[i][k], p));
      }
    }
  }
  for (int i = d + 1; ~i; --i) {
    for (int j = i + 1; j <= d + 1; ++j) {
      sub(a[i][d + 2], mul(a[i][j], a[j][d + 2]));
    }
    a[i][d + 2] = mul(a[i][d + 2], qpow(a[i][i], mod - 2));
  }
}

int g(int i, int j) {
  int p_i = p[j], alpha_i = alpha[j];
  int c1 = (long long) alpha_i * i % (mod - 1);
  int c2 = (c1 + d - i + mod - 1) % (mod - 1);
  return (qpow(p_i, c1) - qpow(p_i, c2) + mod) % mod;
}

int main() {
  scanf("%d%d", &d, &w);
  get_coefficient();
  for (int i = 1; i <= w; ++i) {
    scanf("%d%d", &p[i], &alpha[i]);
  }
  int answer = 0;
  for (int i = 0; i <= d + 1; ++i) {
    int result = a[i][d + 2];
    for (int j = 1; j <= w; ++j) {
      result = mul(result, g(i, j));
    }
    add(answer, result);
  }
  printf("%d\n", answer);
  return 0;
}