Problem
于神之怒加强版
TimeLimit:80Sec
MemoryLimit:512MB
Description
给下N,M,K,计算∑i=1n∑j=1mgcd(i,j)kmod(109+7)的值。
输入有多组数据,输入数据的第一行两个正整数T,K,代表有T组数据,K的意义如上所示,下面第2行到第T+1行,每行为两个正整数N,M,其意义如上式所示。
Output
对于每一个询问,输出一行一个数作为回答。
Sample Output
HINT
1≤N,M,K≤5×106,1≤T≤2000
官方题解
标签:莫比乌斯反演
Solution
先套路转换出μ:
Ans=i=1∑nj=1∑mgcd(i,j)k=d=1∑min(n,m)dki=1∑nj=1∑m[gcd(i,j)=d]=d=1∑min(n,m)dki=1∑⌊dn⌋j=1∑⌊dm⌋x∣gcd(i,j)∑μ(x)=d=1∑min(n,m)dkx=1∑⌊dmin(n,m)⌋μ(x)⌊x⋅dn⌋⌊x⋅dm⌋=t=1∑min(n,m)d=1∑tdkμ(dt)⌊tn⌋⌊tm⌋=t=1∑min(n,m)⌊tn⌋⌊tm⌋d=1∑tdkμ(dt)
那么每次询问对于前半部分可以根号分块,随后需要O(1)计算后半部分的值,因而需要线筛预处理后半部分的值。
令f(t)=∑d=1tdkμ(dt),t=p1a1×p2q2×⋯plal
由于y=dk和y=μ(d)均为积性函数,因而f也为积性函数。
那么就有
f(t)=i=1∏lf(piai)=i=1∏lj=0∑aipij⋅kμ(pijpiai)=i=1∏lj=0∑aipij⋅kμ(piai−j)
易知当j∈[0,ai−1)时,均有μ(piai−j)=0,因此有
f(t)=i=1∏lj=0∑aipij⋅kμ(piai−j)=i=1∏l(μ(pi)⋅pik⋅(ai−1)+μ(1)⋅pik⋅ai)=i=1∏l(pik⋅ai−pik⋅(ai−1))=i=1∏lpik⋅(ai−1)(pi−1)
接下来考虑在线筛中如何处理。
首先,对于所有质数,均有f(p)=pk−1。
而对于合数,假设当前筛到的数是x,对于一个比它小的素数p,有两种情况:
- 若p∣x,设p在x分解质因数中的次数为a,那么f(x×p)相比于f(x)而言,在含p的约数中p的次数都增加了1,否则μ=0无贡献。因此k⋅(a−1)增加了k,这样总共扩大了pk倍,故f(x×p)=f(x)×pk;
- 若p∤x,由积性函数可知f(x×p)=f(x)×f(p)。
这样就可以O(nlogn)筛出f的函数值,每次询问O(Tn)根号分块,总复杂度O(nlogn+Tn)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <bits/stdc++.h> #define MAX_N 5000000 #define MOD 1000000007 using namespace std; typedef long long lnt; template <class T> inline void read(T &x) { x = 0; int c = getchar(), f = 1; for (; !isdigit(c); c = getchar()) if (c == 45) f = -1; for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0'); } lnt k, cnt, ans, f[MAX_N+5], p[MAX_N+5], pri[MAX_N+5]; bool NotPri[MAX_N+5]; lnt PM(lnt x, lnt y) {if (!y) return 1LL; lnt ret = PM(x, y>>1); return (y&1) ? ret*ret%MOD*x%MOD : ret*ret%MOD;} void init() { NotPri[1] = true, f[1] = 1; for (int i = 2; i <= MAX_N; i++) { if (!NotPri[i]) pri[cnt++] = i, p[i] = PM(i, k), f[i] = p[i]-1; for (int j = 0; j < cnt; j++) { if (i*pri[j] > MAX_N) break; NotPri[i*pri[j]] = true; if (i%pri[j]) f[i*pri[j]] = f[i]*f[pri[j]]%MOD; else {f[i*pri[j]] = f[i]*p[pri[j]]%MOD; break;} } } for (int i = 2; i <= MAX_N; i++) (f[i] += f[i-1]) %= MOD; } int main() { int T; read(T), read(k), init(); while (T--) { lnt n, m; read(n), read(m), ans = 0; for (lnt l = 1, r; l <= min(n, m); l = r+1) r = min(n/(n/l), m/(m/l)), (ans += (n/l)*(m/l)%MOD*(f[r]-f[l-1]+MOD)%MOD) %= MOD; printf("%lld\n", ans); } return 0; }
|