1. 程式人生 > 實用技巧 >題解-UVA12995 【Farey Sequence】

題解-UVA12995 【Farey Sequence】

\(\large{\texttt{UVA12995}}\)

演算法:線性篩求尤拉函式,本來還想杜教篩

本文主要給剛學線性篩尤拉函式的萌新看。神仙勿噴QwQ


\(\large{\texttt{Meaning}}\)

給定一個 \(n\) ,求 \(F(n)\) :有多少組數對 \([a,b](0<a<b\le n)\) 其中 \(a,b\) 互質。


\(\large{\texttt{Solution}}\)

\(F(n)\) 中的數對歸類,設 \(f(n)\)\(b\)\(n\) 的數對 \([a,b]\) 的個數,發現 \(f\)\(\varphi\) (尤拉函式,即 \(\varphi(n)=\sum_{i=1}^{n} [ \gcd (n,i) ==1]\)

)意義是相同的,那麼即 \(F(n)=\sum^{n}_{i=1} \varphi(n)\)

然後可以用線性篩求出 \(\varphi\) ,再作一次字首和,得到 \(F\) 陣列,結束了。

還是說說怎麼線性篩求尤拉函式吧。


首先,我們有這麼一個性質:

\(\varphi(n)=n*\prod_{p|n} (p-1)/p ~~~(p\in \texttt{prime})\)

先設 \(f(n)=\prod_{p|n} (p-1)/p ~~~(p\in \texttt{prime})\)

若一個質數 \(p\) 整除 \(n\) ,則 \(f(pn)=f(n)\) ,因為 \(pn\)\(n\)

用質因數的種類相同( \(p\) 不是一個新質因數)。

則有

\[\varphi(pn)=\varphi(n)*p \]

若一個質數 \(p\) 不整除 \(n\) ,則 \(f(pn)=f(n)*(p-1)/p\) ,因為p是一個新的質因數。

則有

\[\varphi(pn)=\varphi(n)*p*(p-1)/p= \varphi(n)*(p-1) \]

發現這些式子線上性篩裡面可以使用(每個數只會被篩到一次),只需要添上幾行程式碼,且不會增加複雜度。(具體操作參考程式碼)


\(\large{\texttt{Code}}\)

#include <iostream>
#include <cstdio>
using namespace std;

// #define PB push_back
// #define MP make_pair
// const int M=200010;
#define int long long
// #define us unsigned
// #define LL long long
const int N = 1000000;
// const int mod = 998244353;
int a, ans, prime[N + 10], phi[N + 10], top;

inline void init()//線性篩尤拉函式
{
  phi[1] = 1;//phi[1]較特殊
  for (int i = 2; i <= N; i++)
  {
    if (!phi[i])
    {
      prime[++top] = i;
      phi[i] = i - 1;
    }
    for (int j = 1; j <= top && i * prime[j] <= N; j++)
    {
      if (i % prime[j] == 0)
      {
        phi[i * prime[j]] = phi[i] * prime[j];
        break;
      }
      phi[i * prime[j]] = phi[i] * phi[prime[j]];
    }
  }
  for (int i = 1; i <= N; i++)//字首和
    phi[i] += phi[i - 1];
}

signed main()
{
  init();
  // freopen("in","r",stdin);
  while (~scanf("%lld", &a))
  {
    if (!a)
      break;
    printf("%lld\n", phi[a] - phi[1]);//注意phi[1]取不了。
  }
  return 0;
}

\(\blue{\Large\texttt{My Blog}}\)