【數論】[素數篩,phi]P3601簽到題

題目描述

給出l,r,要求求出\(\sum_{i = l}^r (i - phi[i]) mod 666623333\)

\(1\leq l\leq r\leq 10^{12}\)\(r - l \leq 10^6\)

Solution

對於一個數n,肯定有至少一個小於等於$\sqrt n $的(質)因子。因爲 \(r \leq 10^{12}\),所以可以先預處理\(10^{6}\)以內的素數, 然後利用r - l比較小的條件,一個一個數篩一遍就是了。用每個質因子去篩l - r之間的數,同時求出每個數的phi。對於個別的大於\(\sqrt n\)的因子,最後特判就行了。

\(\phi (x) = n (1 - \frac {1}{p1})(1 - \frac {1}{p2})...(1 - \frac {1}{pk})\)

Code

#include <iostream>
#include <cstdio>
using namespace std;
inline long long read() {
  long long x = 0; int f = 0; char c = getchar();
  while (c < '0' || c > '9') f |= c == '-', c = getchar();
  while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
  return f? -x:x;
}

const int mod = 666623333;
long long l, r, ans;
int pri[1000006], cnt;
long long k[1000006], phi[1000006];//k記錄原來的數
bool b[1000006];
inline void get_pri() {
  for (int i = 2; i <= 1000; ++i)
    if (!b[i]) for(int j = i << 1; j <= 1000000; j += i) b[j] = 1;
  for (int i = 2; i <= 1000000; ++i)
    if (!b[i]) pri[++cnt] = i;
}
int main() {
  get_pri();
  l = read(); r = read();
  for (long long i = l; i <= r; ++i) phi[i - l] = k[i - l] = i;//初始化
  for (int i = 1; i <= cnt; ++i)
    for (long long p = pri[i], j = max(2ll, (l - 1) / p + 1) * p; j <= r; j += p) {
                               //找一個最小的開始篩的數,手推一下
      long long x = j - l;
      phi[x] = phi[x] / p * (p - 1);//計算phi
      while (k[x] % p == 0) k[x] /= p;//爲後面判斷是否有大因子最鋪墊
    }
  for (long long i = l; i <= r; (ans += i - phi[i - l]) %= mod, ++i)//寫的有點非人類hhh
    if (k[i - l] != 1) phi[i - l] = phi[i - l] / k[i - l] * (k[i - l] - 1);
    //特判
  printf("%lld\n", ans);
  return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章