莫比乌斯反演入门
定理
-
\(\sum_{d|n}\mu(d), n=1时为1,n > 1时为0\)
-
设\(F(i)=\sum_{d|i}f(d)\)
形式一
证明:\(f(n)=\sum_{d|n}\mu(d)\sum_{k|\frac{n}{d}}f(k)=\sum_{d|n}f(d)\sum_{k|\frac{n}{d}}\mu(k)\)
\(\sum_{k|\frac{n}{d}}\mu(k)当1=\frac{n}{d},即n=d时才为1,否则为0\)
所以$f(n)=\sum_{d|n}\mu(d)F(\frac {n}{d}) $
还有第二种形式
- $令 n = \Pi_{p_i|n} p_{i} ^ {a_{i}} \(
若m有\)a_i>1\(则\)\mu(i) = 0$
否则
若\(a_i=1\)有奇数个则\(\mu(i) = -1\)
若\(a_i=1\)有偶数个则\(\mu(i) = 1\)
不会证明找规律可证
有些大佬称之为容斥系数
求解u的代码,筛素数法
IL void Prime(){
isprime[1] = mu[1] = 1;
for(RG int i = 2; i < _; i++){
if(!isprime[i]) prime[++cnt] = i, mu[i] = -1;
for(RG int j = 1; j <= cnt && i * prime[j] < _; j++){
isprime[prime[j] * i] = 1;
if(!(i % prime[j])){ mu[i * prime[j]] = 0; break; }
else mu[prime[j] * i] = -mu[i];
}
}
}
应用
1
【HDU1695】GCD
求a<=x<=b,c<=y<=d,a<=x<=b,c<=y<=d
且gcd(x,y)=k的无序数对的个数
其中,你可以假定a=c=1a=c=1
所有数都<=100000<=100000
数据组数<=3000
。
。
。
即求\(gcd(x, y)==1, x<=b/k, y<=d/k\)的无序数对个数
设$f(i)为gcd(x, y) == i \(的有序数对
\)F(i) = ∑i|d f(d) \(显然\)F(i)表示gcd(x, y)==i$的倍数的有序数对
又显然 $F(i) = (b/k/i)*(d/k/i) $
然后用莫比乌斯反演求出 \(f(i)\)
$f(1)=∑_{1|d} \mu(d)F(d) $
因为是无序的,而我们求的是有序的,所以要去重
# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(100010);
IL ll Read(){
RG char c = getchar(); RG ll x = 0, z = 1;
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + c - '0';
return x * z;
}
int mu[_], prime[_], isprime[_], cnt;
IL void Prime(){
isprime[1] = mu[1] = 1;
for(RG int i = 2; i < _; i++){
if(!isprime[i]) prime[++cnt] = i, mu[i] = -1;
for(RG int j = 1; j <= cnt && i * prime[j] < _; j++){
isprime[prime[j] * i] = 1;
if(!(i % prime[j])){ mu[i * prime[j]] = 0; break; }
else mu[prime[j] * i] = -mu[i];
}
}
}
IL ll Calc(RG ll x, RG ll y){
RG ll ans = 0, _ans = 0, z = min(x, y);
for(RG ll i = 1; i <= z; i++) ans += mu[i] * (x / i) * (y / i);
for(RG ll i = 1; i <= z; i++) _ans += mu[i] * (z / i) * (z / i);
return ans - _ans / 2;
}
int main(RG int argc, RG char* argv[]){
Prime();
RG int n = Read(), x, y, d, Case = 0;
while(n--){
Read(), x = Read(), Read(), y = Read(), d = Read();
printf("Case %d: %lld\n", ++Case, d ? Calc(x / d, y / d) : 0);
}
return 0;
}
2
[POI2007]ZAP-Queries
对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d
(1≤d≤a,b≤50 000) (1≤n≤50 000)
。
。
。
和上面的题目是一样的,只是这个是有序的,数据变大了直接蒯会TLE
因为\(n/d\)的个数只有根号个,所以,在某一段区间里面\(n/i\)是一样的,这时候我们用\((n/(n/i))\)优化去算就好了 预处理\(\mu(i)\)的前缀和)
那么我们可以把连续的一段d一起来算(分块):
设\(a/d=x\),那么最后一个\(a/d=x即d=a/x\),所以这段连续的区间就是\([d,a/(a/d)]\)
# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(100010);
IL ll Read(){
RG char c = getchar(); RG ll x = 0, z = 1;
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + c - '0';
return x * z;
}
int mu[_], prime[_], isprime[_], cnt;
ll sum[_];
IL void Prime(){
isprime[1] = sum[1] = mu[1] = 1;
for(RG int i = 2; i < _; i++){
if(!isprime[i]) prime[++cnt] = i, mu[i] = -1;
for(RG int j = 1; j <= cnt && i * prime[j] < _; j++){
isprime[prime[j] * i] = 1;
if(!(i % prime[j])){ mu[i * prime[j]] = 0; break; }
else mu[prime[j] * i] = -mu[i];
}
}
for(RG int i = 2; i < _; i++) sum[i] = sum[i - 1] + mu[i];
}
IL ll Calc(RG ll x, RG ll y){
RG ll ans = 0, z = min(x, y);
for(RG ll i = 1, j; i <= z; i = j + 1){
j = min(x / (x / i), y / (y / i));
ans += (sum[j] - sum[i - 1]) * (x / i) * (y / i);
}
return ans;
}
int main(RG int argc, RG char* argv[]){
Prime();
RG int n = Read(), x, y, d;
while(n--){
x = Read(), y = Read(), d = Read();
printf("%lld\n", d ? Calc(x / d, y / d) : 0);
}
return 0;
}
3
[HAOI2011]Problem b
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000
。
。
。
一样的再加上一个容斥即可
# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(100010);
IL ll Read(){
RG char c = getchar(); RG ll x = 0, z = 1;
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + c - '0';
return x * z;
}
int mu[_], prime[_], isprime[_], cnt;
ll sum[_];
IL void Prime(){
isprime[1] = sum[1] = mu[1] = 1;
for(RG int i = 2; i < _; i++){
if(!isprime[i]) prime[++cnt] = i, mu[i] = -1;
for(RG int j = 1; j <= cnt && i * prime[j] < _; j++){
isprime[prime[j] * i] = 1;
if(!(i % prime[j])){ mu[i * prime[j]] = 0; break; }
else mu[prime[j] * i] = -mu[i];
}
}
for(RG int i = 2; i < _; i++) sum[i] = sum[i - 1] + mu[i];
}
IL ll Calc(RG ll x, RG ll y){
RG ll ans = 0, z = min(x, y);
for(RG ll i = 1, j; i <= z; i = j + 1){
j = min(x / (x / i), y / (y / i));
ans += (sum[j] - sum[i - 1]) * (x / i) * (y / i);
}
return ans;
}
int main(RG int argc, RG char* argv[]){
Prime();
RG int n = Read(), a, b, c, d, k;
while(n--){
a = Read(), b = Read(), c = Read(), d = Read(), k = Read();
printf("%lld\n", Calc(b / k, d / k) - Calc((a - 1) / k, d / k) - Calc((c - 1) / k, b / k) + Calc((a - 1) / k, (c - 1) / k));
}
return 0;
}
以上就是本蒟蒻的一些见解
题目可能比较水