数学:莫比乌斯反演

首先介绍一下莫比乌斯函数的形式

1.    当d=1d=1时,μ(d)=1
2.    当d=p1*p2*p3*…*pk且pi为互异素数时,μ(d)=(−1)^k。(说直白点,就是d分解质因数后,没有幂次大于平方的质因子,此时函数值根据分解的个数决定)
3.    只要当d含有任何质因子的幂次大于2,则函数值为0

 

在线性筛(欧拉筛法)的基础之上稍加修改就可以得到筛莫比乌斯函数的,函数

 1 #include<cstdio>
 2 const int maxn=105;
 3 int cnt;
 4 bool vis[maxn];
 5 int mu[maxn],prim[maxn];
 6 void get_mu(int n)
 7 {
 8     mu[1]=1;
 9     for(int i=2;i<=n;i++)
10     {
11         if(!vis[i]) {prim[++cnt]=i;mu[i]=-1;}
12         for(int j=1;j<=cnt&&prim[j]*i<=n;j++)
13         {
14             vis[prim[j]*i]=1;
15             if(i%prim[j]==0) break;
16             else mu[i*prim[j]]=-mu[i];
17         }
18     }
19 }
20 int main()
21 {
22     get_mu(100);
23     for(int i=1;i<=100;i++)
24         printf("%d ",mu[i]);
25     return 0;
26 }

 

还有其变式形式:

据说这种形式更加常用哦

BZOJ2301,它的题意是这样的,

对于给出的 n 个询问,每次求有多少个数对(x,y),满足 a≤x≤b, c≤y≤d,且 gcd(x,y) = k,

gcd(x,y)函数为 x 和 y 的最大公约数

设calc(n,m)表示在1<=x<=n,1<=y<=m,满足gcd(x,y)是k的(x,y)的对数

那么当限定区间在(a,b)和(c,d)的时候,可以根据容斥原理确定出来

 calc(b,d)−calc(a−1,d)−calc(b,c−1)+calc(a−1,c−1)

那么对于求每一个calc(x,y)

首先要明确的是求gcd(x,y)=k就是求gcd(x/k,y/k)=1的解

然后设f(i)为gcd(x,y)=i时(x,y)的对数,F(i)表示满足i|gcd(x,y)的(x,y)的对数,显然F(i)=[n/i][m/i] 这里[]就是向下取整

F函数很容易求得,然后我们用莫比乌斯反演公式来求那个f函数就好了

然后在计算向下取整的时候,需要用到一个整除分块的东西提高效率

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn=50005;
 5 int tot;
 6 bool mark[maxn];
 7 int sum[maxn],mu[maxn],pri[maxn];
 8 inline int read()
 9 {
10     int x=0,f=1;char ch=getchar();
11     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
13     return x*f;
14 }
15 void getmu()
16 {
17     mu[1]=1;
18     for(int i=2;i<=50000;i++)
19     {
20         if(!mark[i]) {mu[i]=-1;pri[++tot]=i;}
21         for(int j=1;j<=tot&&i*pri[j]<=50000;j++)
22         {
23             mark[i*pri[j]]=1;
24             if(i%pri[j]==0) {mu[i*pri[j]]=0;break;}
25             else mu[i*pri[j]]=-mu[i];
26         }
27     }
28     for(int i=1;i<=50000;i++)
29         sum[i]=sum[i-1]+mu[i];
30 }
31 int cal(int n,int m)
32 {
33     if(n>m) swap(n,m);
34     int ans=0,pos;
35     for(int i=1;i<=n;i=pos+1)
36     {
37         pos=min(n/(n/i),m/(m/i));
38         ans+=(sum[pos]-sum[i-1])*(n/i)*(m/i);
39     }
40     return ans;
41 }
42 int main()
43 {
44     int T;
45     int a,b,c,d,k;
46     getmu();
47     T=read();
48     while(T--)
49     {
50         a=read();b=read();c=read();d=read();k=read();
51         a--;c--;
52         a/=k;b/=k;c/=k;d/=k;
53         int ans=cal(a,c)+cal(b,d)-cal(a,d)-cal(b,c);
54         printf("%d\n",ans);
55 }
56     return 0;
57 }

 

posted @ 2018-09-02 14:00  静听风吟。  阅读(181)  评论(0编辑  收藏  举报