对于正整数n,定义f(n)为n所含质因子的最大幂指数。例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0。 给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b)。
Input
第一行一个数T,表示询问数。 接下来T行,每行两个数a,b,表示一个询问。
Output
对于每一个询问,输出一行一个非负整数作为回答。
Sample Input
4 7558588 9653114 6514903 4451211 7425644 1189442 6335198 4957
Sample Output
35793453939901 14225956593420 4332838845846 15400094813
Hint
【数据规模】
T<=10000
1<=a,b<=10^7
题目大意 定义函数为数x所含质因子的最大幂指数。求。
继续用之前的套路统计每个f被计算的次数,然后将gcd化为莫比乌斯函数之和。
如果这样,似乎并没有什么好方法求值,所以试试把那几个向下取整的分数搞到外层来,这样就可以找机会分段处理。于是得到了下面这个式子:
好了问题来了,如何把后面的值快速预处理出来,前面的是可以通过分段降低时间复杂度。
为了方便描述,所以我们设。
为了更好地理解证明,你需要清楚一个东西,就是对于一个非空有限集合S,满足。至于它的证明,考虑S的k元素子集的个数,然后二项式定理搞成(1-1)的某次幂。
由于我比较笨,不会大佬们的分类讨论做法。就用集合论乱搞骗结论。
首先对n进行质因数分解,得到。然后设,(是P‘的补集)。
根据莫比乌斯函数的性质,对于任何一个数有一个质因子的幂指数大于1,则它的莫比乌斯函数值为0,即为了使式子对答案有贡献,就应当满足,当d分解后,pi的幂指数要么是ai要么是ai - 1然后有
式子中A的意义大概就是它里面的质因子pi的幂指数就是ai - 1.
然后根据这个又可以得到一个事实,中间的max一坨的取值要么是a要么是(a - 1)。所以我们可以把它拆成两部分求和。
一部分是包含整个P‘,所以此时那一坨的取值为a-1,另一部分是只包含P‘的一部分,所以此时那一坨的取值为a。
然后前一部分事实上可以把A拆成的子集和P‘的并集,后一部分也可以把A拆成P‘的真子集和的子集。
然后发现左右两边的求和符号可以合并。于是得到了
接下来考虑。
1) 如果它不为空的话,那么意味着求和符号下的值为0。换言之,就是n中的所有幂指数不相等,那么就有。
2) 如果它为空的话,意味着求和符号的值为1,P‘的大小就是k,那么就有。
然后有什么用呢?
考虑预处理,由于1e7的凶残数据范围所以不能够O(nlogn)预处理。就考虑线性筛。
这里要用到线性筛一个很重要的性质,每个合数保证被其最小的质因子筛掉。所以,我们只需要记录每个数的最小质因子的次数pos。
这样在prime[j]不整除i的时候我们就可以轻松处理掉(判断最小质因子的次数是否为1,g(i)是否为0)。
现在考虑prime[j]整除i的时候,通过常用套路,我们考虑i把prime[j]除尽后的数x和 i / x * prime[j]。此时显然可以判断一下pos[x]和pos[i] + 1是否相等,更新g值就好了。
因此,我们还需要记录这个数的最小质因子去除这个数,直到不能再除后得到的数。这些记录的东西都可以在线性筛中得到(假设读者都会,就直接贴代码了)。
我表示这是有史以来我写过最长的题解和最短的模板(bits/std++.h直接抵掉我15行左右的include)。
Code
1 /** 2 * bzoj 3 * Problem#3309 4 * Accepted 5 * Time:11804ms 6 * Memory:170040k 7 */ 8 #include <bits/stdc++.h> 9 #ifndef WIN32 10 #define Auto "%lld" 11 #else 12 #define Auto "%I64d" 13 #endif 14 using namespace std; 15 typedef bool boolean; 16 17 #define LL long long 18 19 const int limit = 1e7; 20 21 int num = 0; 22 int prime[700100]; 23 boolean vis[limit + 1]; 24 int last[limit + 1]; 25 int posm[limit + 1]; 26 LL g[limit + 1]; 27 inline void Euler() { 28 memset(vis, false, sizeof(vis)); 29 g[0] = g[1] = -1; 30 for(int i = 2; i <= limit; i++) { 31 if(!vis[i]) prime[num++] = i, last[i] = 1, posm[i] = 1, g[i] = 1; 32 for(int j = 0; j < num && i * 1LL * prime[j] <= limit; j++) { 33 int c = i * prime[j]; 34 vis[c] = true; 35 if(i % prime[j]) { 36 last[c] = i, posm[c] = 1; 37 g[c] = (posm[i] == 1) ? (-g[i]) : (0); 38 } else { 39 last[c] = last[i]; 40 posm[c] = posm[i] + 1; 41 g[c] = (posm[last[c]] == posm[c] || last[c] == 1) ? (-g[last[c]]) : (0); 42 break; 43 } 44 } 45 } 46 for(int i = 2; i <= limit; i++) 47 g[i] += g[i - 1]; 48 } 49 50 int a, b; 51 inline void init() { 52 scanf("%d%d", &a, &b); 53 } 54 55 inline void solve() { 56 if(a > b) swap(a, b); 57 long long res = 0; 58 for(int i = 1, j; i <= a; i = j + 1) { 59 j = min(a / (a / i), b / (b / i)); 60 res += (a / i) * 1LL * (b / i) * (g[j] - g[i - 1]); 61 } 62 printf(Auto"\n", res); 63 } 64 65 int T; 66 int main() { 67 Euler(); 68 scanf("%d", &T); 69 while(T--) { 70 init(); 71 solve(); 72 } 73 return 0; 74 }