鉴于容易忘,决定先把目前会的写出来.....
莫比乌斯函数:
对于一个整数N,按照算数基本定理分解质因数为N = p1^c1 * p2^c2 * p3^c3 * ... * pm^cm
0 存在i∈[1, m],ci>1
μ(N) = { 1 m≡0(mod 2),任意i∈[1, m],ci = 1
-1 m≡1(mod 2),任意i∈[1, m],ci = 1
通俗讲:
①当N包含相等的质因子时,μ(N) = 0
②当N的所有质因子各不相等时,若N有偶数个质因子,μ(N) = 1
③当N有奇数个质因子时,μ(N) = -1
当然百度的话显然能找到更好的说明方式,但是我觉得这样比较清楚(虽然不利于理解求莫比乌斯函数)
莫比乌斯函数求法
1 memset(vis, 0, sizeof(vis)); 2 miu[1] = 1, vis[1] = 1; 3 for(int i = 2; i < v; ++i) { 4 if(!vis[i]) prime[++cnt] = i, miu[i] = -1; 5 for(int j = 1; j <= cnt; ++j) { 6 if(prime[j] > v / i) break; 7 vis[i * prime[j]] = 1; 8 if(i % prime[j] == 0) {//说明对于i * prime[j], 其中prime[j]的幂大于1 9 miu[prime[j] * i] = 0; 10 break; 11 } 12 miu[prime[j] * i] = -miu[i]; 13 } 14 }
莫比乌斯反演:
<1>莫比乌斯函数的一些性质:
·对于任意正整数n,Σμ(d) = [n=1] (d|n)
证明:当n = 1时,显然有函数g(n) = Σμ(d) = 1 (d|n)
当n = p^k时(p为质数)
g(n) = Σμ(d) = μ(1)+μ(p)+μ(p^2)+...+μ(p^k)
= 1 + (-1) + 0 + ... + 0 = 0
当n = p1^c1 * p2^c2 * ... * pk^ck时
g(n) = g(p1^c1)g(p2^c2)....g(pk^ck) = 0
·对于任意正整数n,Σμ(d)/d = φ(n)/n (d|n) //然而我并不会证明
<2>莫比乌斯反演
定理:F(n), f(n)为两个非负整数集合上的函数
F(n) = Σf(d) (d|n) <==> f(n) = ΣΣμ(d)*f(e) (前一个Σ范围为d|n,后一个Σ范围为e|(n/d))
证明:f(n)=Σμ(d)F(n/d) = Σμ(d)(d|n) * Σf(e)(e|(n/d)) = Σ(d|n)Σ(e|n/d)μ(d)*f(e)
交换求和顺序有:f(n) = Σf(e)(e|n)Σμ(d)(d|n/e)
根据莫比乌斯函数性质1,当且仅当n/e == 1,也就是n = e时,Σμ(d) = 1(d|n/e)
则f(n) = f(e)*1 = f(n)
证毕
(关于莫比乌斯反演,还有狄利克雷卷积证法,但是我不会.jpg)
莫比乌斯反演公式图片搬运版(因为我根本不会markdown语法)
大佬眼中的入门题:
Bzoj2301
以cal(a, b, k)表示对于x<=a, y<=b,gcd(x, y)=k的x, y的对数
则我们求的是
ΣΣ[gcd(i,j)==k](1<=i<=n, 1<=j<=m)
则根据容斥原理,答案显然为
cal(b, d, k) - cal(a - 1, d, k) - cal(b, c - 1, k) + cal(a - 1, b - 1, k)
/*
对于问题ΣΣ[gcd(i,j)==k](1<=i<=n, 1<=j<=m),可以转化为
求对于1<=x<=[n/k],1<=y<=[m/k],gcd(x,y)互质的对数,也就是
ΣΣ[gcd(i,j)==1](1<=i<=[n/k], 1<=j<=[m/k])
实际上莫比乌斯反演式的本质就是Σμ(d)=[n==1] (d|n)
因此有Σμ(d)=[gcd(i,j)==1] (d|gcd(i,j))
因此也就是求
ΣΣΣμ(d) (1<=i<=[n/k], 1<=j<=[m/k], d|gcd(i,j))
.....
这些是另一个常见推法的基本操作,但是我不会和式变换所以我不会这么推....
*/
f(i)表示1<=x<=n, 1<=y<=m, 且gcd(x,y)=i的数对(x,y)的个数
F(i)表示1<=x<=n, 1<=y<=m, 且i|gcd(x,y)的数对(x,y)的个数
则F(i)=[n/i]*[m/i],
F[i]=Σf(d)(d|i)
→f(i)=Σμ(d/i)F(d) (i|d)
→f(i)=Σμ(d/i)[n/d][m/d] (i|d)
对于题目,我们只需要求f(k),然后枚举d(d为k的倍数)
也就是枚举k的每一个倍数可以在O(n)复杂度内回答一次询问,显然不能通过题目
考虑优化,容易注意到:我们对于k的倍数的枚举取决于[n/d]和[m/d]的取值
首先分析[n/d]的取值
1)当1<=d<=[sqrt(n)]时,最多有[sqrt(n)]种不同的取值
2)当[sqrt(n)]+1<=d<=n是,由于[n/d]<=[sqrt(n)],因此有[sqrt(n)]种不同的取值
综上所述+同理:
[n/d]有2[sqrt(n)]种不同的取值
[m/d]有2[sqrt(m)]种不同的取值
那么[n/d][m/d]有多少种不同的取值呢
首先,不是4[sqrt(n)][sqrt(m)]个
考虑在一个数轴上,[n/d]将一段变成了2[sqrt(n)]个块,每个块内取值相同,表示有2[sqrt(n)]中取值
同理考虑[m/d],然后手画一个图,可以发现,对于[n/d]划出的块和[m/d]划出的块的间断点是不同的
将[n/d]和[m/d]合并,其实是间断点的合并,即:块的数量变为了2[sqrt(n)]+2[sqrt(m)]
也就是说[n/d][m/d]一共有2[sqrt(n)]+2[sqrt(m)]个取值
对莫比乌斯函数维护前缀和,对于每一段就可以直接回答了
令n <= m这样对于每次询问,复杂度为O(sqrt(n))
总复杂度就是O(q*sqrt(n))
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define uint unsigned int 4 #define ull unsigned long long 5 using namespace std; 6 const int maxn = 50010; 7 int miu[maxn], sum_miu[maxn]; 8 int T, a, b, c, d, k, cnt = 0; 9 int prime[maxn], vis[maxn]; 10 11 inline int read() { 12 int x = 0, y = 1; 13 char ch = getchar(); 14 while(!isdigit(ch)) { 15 if(ch == '-') y = -1; 16 ch = getchar(); 17 } 18 while(isdigit(ch)) { 19 x = (x << 1) + (x << 3) + ch - '0'; 20 ch = getchar(); 21 } 22 return x * y; 23 } 24 25 inline void pre_miu(int v) { 26 memset(vis, 0, sizeof(vis)); 27 miu[1] = 1, vis[1] = 1; 28 for(int i = 2; i < v; ++i) { 29 if(!vis[i]) prime[++cnt] = i, miu[i] = -1; 30 for(int j = 1; j <= cnt; ++j) { 31 if(prime[j] > v / i) break; 32 vis[i * prime[j]] = 1; 33 if(i % prime[j] == 0) {//说明对于i * prime[j], 其中prime[j]的幂大于1 34 miu[prime[j] * i] = 0; 35 break; 36 } 37 miu[prime[j] * i] = -miu[i]; 38 } 39 } 40 for(int i = 2; i < v; ++i) miu[i] += miu[i - 1]; 41 } 42 43 inline ll calc(int n, int m, int k) {//枚举不同的值,last指向下一个间断点,这个方法又被称为数论分块 44 n /= k, m /= k; 45 if(n > m) swap(n, m); 46 int last = 0; ll res = 0; 47 for(int i = 1; i <= n; i = last + 1) { 48 last = min(n / (n / i), m / (m / i)); 49 res += 1LL * (n / i) * (m / i) * (miu[last] - miu[i - 1]); 50 } 51 return res; 52 } 53 54 int main() { 55 T = read(); 56 pre_miu(maxn); 57 while(T--) { 58 a = read(), b = read(), c = read(), d = read(), k = read(); 59 ll ans = calc(b, d, k) - calc(a - 1, d, k) - calc(b, c - 1, k) + calc(a - 1, c - 1, k); 60 printf("%lld\n", ans); 61 } 62 return 0; 63 }
后续的题目回头慢慢补咕咕咕....