swust 13th 校赛 小C的素数问题【莫比乌斯反演】

https://www.oj.swust.edu.cn/problem/show/2863

2863: 小C的素数问题

Time Limit: 6000 MS Memory Limit: 2097152 KB
Total Submit: 15 Accepted: 4 Page View: 39
Submit Status Discuss

Description 

小 CC 研究数学日渐憔悴,他最近对素数十分感兴趣。

他定义 W(n)=2k(n)W(n)=2k(n),其中 k(n)k(n) 表示 nn 的不同素因子的个数。

换句话说:n=∏k(n)i=1paiin=∏i=1k(n)piai,pipi 是素数。比如:W(12)=22W(12)=22,12=22×312=22×3  。

他想要知道 F(n)=∑ni=1∑nj=1W(gcd(i,j))F(n)=∑i=1n∑j=1nW(gcd(i,j)) 的值, gcd(i,j)gcd(i,j) 表示 ii 和 jj 的最大公约数。


你能帮帮他吗?由于F(n)F(n) 可能很大,你只需要输出 F(n)F(n) 对 KK取模的结果。

Input 

第一行包含一个整数 T (1≤T≤200)T (1≤T≤200) 代表测试组数,对于每一组测试:
第一行两个整数 nn,KK (1≤n≤1011,1≤K≤2×109)(1≤n≤1011,1≤K≤2×109) 含义如上所述 。
数据保证所有测试中只有两组数据 n>109n>109。

Output 

对于每组测试,输出一行,包含一个整数,表示 F(n)F(n)模KK。

5 1 10 3 10 4 10 134 1000 666 20000

1 1 1 909 12201

分析:

首先这个W(n)是一个很烦的东西,先不管。

设:

F(n)表示gcd(i,j)==n及n的倍数的个数

f(n)表示gcd(i,j)==n的个数

推式子:

F(n)=\sum _{n|d}f(d)=(N/n)^2

f(n)=\sum _{n|d}u(d/n)*F(d)= \sum _{i=1}^{N}\sum _{j=1}^{N}(gcd(i,j)==n)

ans=\sum _{n=1}^{N}W(n)*\sum _{i=1}^{N}\sum _{j=1}^{N}(gcd(i,j)==n)

ans=\sum _{n=1}^{N}f(n)*W(n)

ans=\sum _{n=1}^{N}\sum _{n|d}u(d/n)(N/d)^2*W(n)

这个n看起不顺眼,于是换成i

ans=\sum _{i=1}^{N}\sum _{i|d}u(d/i)(N/d)^2*W(i)

现在发现推不下去了,来考虑W(i)。

莫比乌斯函数u(n)的定义:若n可分为若干个不同的素因子,则u(n)=1或-1,否则=0。

W(n)的定义:W(n)=2^k(n),其中 k(n)表示 n 的不同素因子的个数。

发现十分相似对吧?

令m=k(n) 有:

W(n)=2^m=\sum _{i=0}^mC_{m}^{i}

那么翻译一下W(n)的定义:从n的不同的素因子中,选取任意多个素因子,有多少种取法。

选取出来的素因子组成的数及是n的因子,仔细推敲一番,有:

W(n)=\sum _{d|n}u^2(d)

此处u取平方是为了去掉负号。

接下来发现之前的式子也有点眼熟,继续推:

交换和号

ans=\sum _{d=1}^{N}\sum _{i|d}(N/d)^2*u(d/i)*W(i)

ans=\sum _{d=1}^{N}(N/d)^2\sum _{i|d}u(d/i)*W(i)

后面一部分就是莫比乌斯反演后的形式,只需要将其还原即可。

W(n)=\sum _{d|n}u^2(d)

u^2(n)=\sum _{d|n}u(d)*W(n/d)

代换一下变量就可以变成如下形式

u^2(n)=\sum _{d|n}u(n/d)*W(d)

ans=\sum _{d=1}^{N}(N/d)^2*u^2(d)

前面部分直接分块就可以,此时还需要用到一个公式(不会证明,推到这里就推不动了,看了题解才知道还有这种东西。。。)

\sum _{i=1}^nu^2(i)=\sum _{i=1}^{sqrt(n)}u(i)*(n/i^2)

然后两次整数分块得到ans。

复杂度O(n^{3/4})

然后直接求解也是不行的,需要一点莫名其妙的优化才能过题。也许我是大常数选手

#include "bits/stdc++.h"

using namespace std;
const int maxn = 20000004;
bool vis[maxn];
int prim[maxn];
int mu[maxn];
int pre[maxn];//对于较小的u(n),直接打表,在询问时可以O(1)得到,必然可以节省很多时间
int cnt = 0;
long long mod;

void init() {
    memset(mu, 0, sizeof(mu));
    memset(vis, 0, sizeof(vis));
    cnt = 0;
    mu[1] = 1;
    for (int i = 2; i < maxn; ++i) {
        if (!vis[i]) {
            prim[cnt++] = i;
            mu[i] = -1;
        }
        for (int j = 0; j < cnt && i * prim[j] < maxn; ++j) {
            vis[i * prim[j]] = 1;
            if (i % prim[j] == 0)break;
            else mu[i * prim[j]] = -mu[i];
        }
    }
    for (int i = 1; i < maxn; ++i) {
        pre[i] = pre[i - 1] + abs(mu[i]);
    }
    for (int i = 1; i < maxn; ++i) {
        mu[i] += mu[i - 1];
    }
}

unordered_map<long long, long long> mp;//只有200组数据,mp记忆化一下,注意这里不能求mod,因为不同数据的mod不一样

long long getsum(long long n) {
    if (n < maxn)return pre[n];
    if (mp.count(n))return mp[n];
    long long res = 0;
    for (long long l = 1, r; l * l <= n; l = r + 1) {
        r = sqrt(n / (n / (l * l)) + 0.5);
        res = (res + n / (l * l) * (mu[r] - mu[l - 1])) ;
    }
    return res;
}

int main() {
    int t;
    cin >> t;
    init();
    while (t--) {
        long long n;
        cin >> n >> mod;
        long long ans = 0;
        for (long long l = 1, r; l <= n; l = r + 1) {
            r = n / (n / l);
            ans = (ans + (n / l) % mod * ((n / l) % mod) % mod * ((getsum(r) - getsum(l - 1) + mod) % mod) % mod) % mod;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42671946/article/details/89318300