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的个数
推式子:
这个n看起不顺眼,于是换成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)的定义:从n的不同的素因子中,选取任意多个素因子,有多少种取法。
选取出来的素因子组成的数及是n的因子,仔细推敲一番,有:
此处u取平方是为了去掉负号。
接下来发现之前的式子也有点眼熟,继续推:
交换和号
后面一部分就是莫比乌斯反演后的形式,只需要将其还原即可。
代换一下变量就可以变成如下形式
前面部分直接分块就可以,此时还需要用到一个公式(不会证明,推到这里就推不动了,看了题解才知道还有这种东西。。。)
然后两次整数分块得到ans。
复杂度
然后直接求解也是不行的,需要一点莫名其妙的优化才能过题。也许我是大常数选手
#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 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;
}