1. 程式人生 > >BZOJ 4804: 歐拉心算 歐拉函數

BZOJ 4804: 歐拉心算 歐拉函數

algo long 分享圖片 data jpg esp aps time output

4804: 歐拉心算

>原題鏈接<

Description

給出一個數字N

技術分享圖片

Input

第一行為一個正整數T,表示數據組數。 接下來T行為詢問,每行包含一個正整數N。 T<=5000,N<=10^7

Output

按讀入順序輸出答案。

Sample Input

1
10

Sample Output

136

思路:

考慮化簡原式,設gcd(i,j) = p 則原式可化為
$\sum\limits_{p=1}^{n} \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} \varphi(p)[gcd(i,j)=p]$
由於$\varphi(p)$和i、j均無關,故我們可以把$\varphi(p)$提到前面化為

$\sum\limits_{p=1}^{n} \varphi(p)\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n}[gcd(i,j)=p]$
考慮把$[gcd(i,j)=p]$ 中的p除到前面,則原式可化為
$\sum\limits_{p=1}^{n} \varphi(p)\sum\limits_{i=1}^{n/p} \sum\limits_{j=1}^{n/p}[gcd(i,j)=1]$
這時我們設一個函數f(n) 表示 $\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=1]$
則原式可視為 $\sum\limits_{p=1}^{n} \varphi(p)*f(n/p)$

考慮化簡函數f(n) 。我們發現這個函數和歐拉函數相當相像。首先我們只考慮$i\ge j$的情況;
  即為 $\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{i}[gcd(i,j)=1] => \sum\limits_{i=1}^{n}\varphi(i)$
  同理,我們發現只考慮$i\le j$的情況,
也為$\sum\limits_{j=1}^{n}\varphi(j)$
兩種情況同時考慮,發現有一種$i=j$的情況重復,故$f(n)$最終可化簡為$2 \times\sum\limits_{i=1}^{n}\varphi(i) -1$
綜上所述,我們可以發現$ans = \sum\limits_{p=1}^{n} \varphi(p) \times ( (2 \times\sum\limits_{i=1}^{n}\varphi(i)) -1)$

現在我們有了一個$O(2\times n)$的方法求解$ans$ 但是數據組數為$T = 5000$ . 我們需要更加優化的方法才能解決問題, 剛剛的2 * n中有一個n是預處理出$\varphi()$以及其前綴和。 是不和T相乘的,故我們考慮把每個答案$O(\sqrt n)$處理
發現在處理s[n/p]中,n/p在每一段的值都是相同的,共有約$\sqrt n$段, 而根據乘法結合律,我們可以先把$\varphi()$加到一起, 再來乘這個相等的s。s是在預處理中做好的, 故總的時間復雜度為$O(2\times n + T\times \sqrt n)$

下面附上代碼
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10000001;
int phi[N+10], pr[N+10], tot;
bool np[N+10];
long long s[N+10];
void getphi() {
    int i, j;
    phi[1] = 1;
    for(i=2;i<=N;i++) {
        if(!np[i]) {
            pr[++tot] = i;
            phi[i] = i-1;
        }
        for(j=1;j<=tot&&i*pr[j]<=N;j++) {
            np[i*pr[j]]=1;
            if(i%pr[j] == 0) {
                phi[i*pr[j]]=phi[i]*pr[j];
                break;
            }
            else
                phi[i*pr[j]]=phi[i]*(pr[j] - 1);
        }
    }
    for(i=1;i<=N;i++) {
        s[i]=s[i-1]+phi[i];
    }
}
int main() {
    int t;
    scanf("%d",&t);
    getphi();
    while(t--) {
        int n;
        long long ans=0;
        scanf("%d",&n);
        int lst =0;
        for(int p=1;p<=n;p=lst+1) {
            lst = n/(n/p);
            ans+=(s[lst]-s[p-1])*(s[n/p]*2-1);
        }
        printf("%lld\n",ans);
    }
    
}

歡迎來原博客看看>原文鏈接<

 特別感謝同屆神犇@fcwww 給我的幫助

BZOJ 4804: 歐拉心算 歐拉函數