牛客练习赛76 F - phi and phi(欧拉函数性质+莫比乌斯反演+差分)

传送门


题目大意

给定 n n n f ( n ) = ∑ i = 1 n ∑ j = 1 n φ ( i j ) φ ( g c d ( i , j ) ) f(n) = \sum_{i=1}^n\sum_{j=1}^n \varphi(ij)\varphi(gcd(i,j)) f(n)=i=1nj=1nφ(ij)φ(gcd(i,j)),求出 [ 1 , n ] [1,n] [1,n]每个数的 f f f值输出。

解题思路

这道题有三个难点,我挂在了第一个…

第一步:问题简化

首先式子里的 φ ( g c d ( i , j ) ) \varphi(gcd(i,j)) φ(gcd(i,j))看着很不爽,不按常理出牌,导致没办法直接进行反演变换。于是必须简化,题解直接给出了这个结论:

φ ( i ∗ j ) = φ ( i ) ∗ φ ( j ) ∗ g c d ( i , j ) φ ( g c d ( i , j ) ) \varphi(i*j) = \frac{\varphi(i)*\varphi(j)*gcd(i,j)}{\varphi(gcd(i,j))} φ(ij)=φ(gcd(i,j))φ(i)φ(j)gcd(i,j)

证明上式需要知道欧拉函数的这个性质,设 p p p为素数, φ ( p a ) = p a − p a − 1 \varphi(p^a)=p^a-p^{a-1} φ(pa)=papa1。简单证明:根据容斥定理,使用 p a p^a pa减去与它不互质的整数个数即可,小于 p a p^a pa且不和 p a p^a pa互质的数的个数就是范围内 p p p的倍数的个数,有 ⌊ p a p ⌋ = p a − 1 \lfloor\frac{p^a}{p} \rfloor=p^{a-1} ppa=pa1个,那么定理得证。根据这个定理,欧拉函数也能通过质因数分解求解。

i i i的质因数分解式子为 p 1 a 1 p 2 a 2 . . . p n a n p_1^{a_1}p_2^{a_2}...p_n^{a_n} p1a1p2a2...pnan j j j的质因数分解式子为 q 1 b 1 q 2 b 2 . . . q n b n q_1^{b_1}q_2^{b_2}...q_n^{b_n} q1b1q2b2...qnbn,设 r r r为二者的公共质因子,其中在 i , j i,j i,j中的幂次为 a , b a,b a,b。那么将 i ∗ j i*j ij质因数分解然后求欧拉函数得到 r r r部分为 φ ( r a + b ) \varphi(r^{a+b}) φ(ra+b),因此 φ ( r a + b ) = r a + b − r a + b − 1 \varphi(r^{a+b}) = r^{a+b}-r^{a+b-1} φ(ra+b)=ra+bra+b1,互质部分互不影响,那么 φ ( r a ) ∗ φ ( r b ) ∗ g c d ( r a , r b ) φ ( g c d ( r a , r b ) ) = r a + b − r a + b − 1 \frac{\varphi(r^a)*\varphi(r^b)*gcd(r^a,r^b)}{\varphi(gcd(r^a,r^b))} = r^{a+b}-r^{a+b-1} φ(gcd(ra,rb))φ(ra)φ(rb)gcd(ra,rb)=ra+bra+b1,上式得证。

第二步:反演

∑ i = 1 n ∑ j = 1 n φ ( i j ) φ ( g c d ( i , j ) ) = ∑ i = 1 n ∑ j = 1 n φ ( i ) φ ( j ) g c d ( i , j ) = ∑ i = 1 n ∑ j = 1 n ∑ d = 1 n [ ( i , j ) = d ] ∗ d ∗ φ ( i ) ∗ φ ( j ) \sum_{i=1}^n\sum_{j=1}^n\varphi(ij)\varphi(gcd(i,j)) \\ = \sum_{i=1}^n\sum_{j=1}^n \varphi(i) \varphi(j) gcd(i,j) \\ = \sum_{i=1}^n\sum_{j=1}^n \sum_{d=1}^n [(i,j) = d]*d * \varphi(i) *\varphi(j) i=1nj=1nφ(ij)φ(gcd(i,j))=i=1nj=1nφ(i)φ(j)gcd(i,j)=i=1nj=1nd=1n[(i,j)=d]dφ(i)φ(j)

将枚举 d d d的循环提前:

∑ d = 1 n d ∑ i = 1 n ∑ j = 1 n [ ( i , j ) = 1 ] ∗ φ ( i ) ∗ φ ( j ) \sum_{d=1}^n d \sum_{i=1}^n \sum_{j=1}^n [(i,j) = 1] * \varphi(i)* \varphi(j) d=1ndi=1nj=1n[(i,j)=1]φ(i)φ(j)

又因为 ∑ d ∣ ( i , j ) μ ( d ) ⟺ [ ( i , j ) = 1 ] \sum_{d|(i,j)} \mu(d) \Longleftrightarrow [(i,j) = 1] d(i,j)μ(d)[(i,j)=1]

∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ φ ( i ∗ d ) ∗ φ ( j ∗ d ) ∑ k ∣ ( i , j ) μ ( k ) = ∑ d = 1 n d ∑ k = 1 ⌊ n d ⌋ μ ( k ) ∗ [ ∑ i = 1 ⌊ n d k ⌋ φ ( i d k ) ] 2 \sum_{d=1}^n d \sum_{i=1}^{\lfloor \frac{n}{d}\rfloor} \sum_{j=1}^{\lfloor \frac{n}{d}\rfloor} \varphi(i*d)* \varphi(j*d) \sum_{k|(i,j)} \mu(k) \\ =\sum_{d=1}^n d \sum_{k = 1}^{\lfloor \frac{n}{d}\rfloor} \mu(k) *[\sum_{i=1}^{\lfloor \frac{n}{dk}\rfloor} \varphi(idk)]^2 d=1ndi=1dnj=1dnφ(id)φ(jd)k(i,j)μ(k)=d=1ndk=1dnμ(k)[i=1dknφ(idk)]2

T = k d , d ∣ T T = kd,d|T T=kddT,那么改为枚举 T ∈ [ 1 , n ] T \in [1,n] T[1,n] ∑ T = 1 n ∑ d ∣ T d ∗ μ ( T d ) ∗ [ ∑ i = 1 ⌊ n T ⌋ φ ( i ∗ T ) ] 2 \sum_{T=1}^n \sum_{d|T}d* \mu(\frac{T}{d}) *[\sum_{i=1}^{\lfloor \frac{n}{T}\rfloor} \varphi(i*T)]^2 T=1ndTdμ(dT)[i=1Tnφ(iT)]2

又因为 φ ( n ) = ∑ d ∣ n μ ( n d ) ∗ d \varphi(n) = \sum_{d|n} \mu(\frac{n}{d})*d φ(n)=dnμ(dn)d,那么最终得到 ∑ T = 1 n φ ( T ) ∗ [ ∑ i = 1 ⌊ n T ⌋ φ ( i ∗ T ) ] 2 \sum_{T=1}^n \varphi(T)* [\sum_{i=1}^{\lfloor \frac{n}{T}\rfloor} \varphi(i*T)]^2 T=1nφ(T)[i=1Tnφ(iT)]2

第三步:求解

但是实际上如果单纯看上式,求出一个数的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),如果求 n n n次显然直接超时,那么就需要找到降低时间复杂度的方法。

观察上式可以得到,对于某个 T T T,那么在 [ i ∗ T , ( i + 1 ) ∗ T ) [i*T, (i+1)*T) [iT,(i+1)T)中间的结果都是一样的,那么累积的贡献可以相当于区间都加上这个结果,即求差分。那么枚举 [ 1 , m a x n ) [1,maxn) [1,maxn)的每个 T T T,不断地更新每个区间的答案,最后只需要对 [ 1 , n ] [1,n] [1,n]求前缀和还原即可。

#include <bits/stdc++.h>

using namespace std;
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 0x7fffffff;
const int Mod = 1e9 + 7;
const int maxn = 1e6 + 10;

ll ans[maxn];
int phi[maxn], prime[maxn];
bitset<maxn> vis;

void euler() {
    phi[1] = 1;
    int cnt = 0;
    for (int i = 2; i < maxn; i++) {
        if (!vis[i]) {
            prime[++cnt] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j <= cnt && 1LL * i * prime[j] < maxn; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j])
                phi[i * prime[j]] = (prime[j] - 1) * phi[i];
            else {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
        }
    }
}

void cal(int n) {
    for (int i = 1; i <= n; i++) {
        ll sum = 0;
        for (int j = 1; j <= n / i; j++) {
            sum = (sum + phi[j * i]) % Mod;
        }
        
    }
}

void solve() {
    for (int i = 1; i < maxn; i++) {
        ll sum = 0;
        for (int l = i, r = 2 * i; l < maxn; l = r, r += i) {
            sum = (sum + phi[l]) % Mod;
            ans[l] = (ans[l] + sum * sum % Mod * phi[i] % Mod) % Mod;
            if (r < maxn)
                ans[r] = (ans[r] - sum * sum % Mod * phi[i] % Mod + Mod) % Mod;
        }
    }
    for (int i = 1; i < maxn; i++) {
        ans[i] = ((ans[i] + ans[i - 1]) % Mod + Mod) % Mod;
    }
}

int main() {
    // ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    euler();
    solve();
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        printf("%lld\n", ans[i]);
    }
    return 0;
}

你可能感兴趣的:(#,积性函数,#,牛客)