莫比乌斯反演 刷题记录

BZOJ1101: [POI2007]Zap

  • 题意:对于给定的整数a,b和d,求有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d
  • 直接求有多少正整数对x,y,gcd(x,y)=d比较难求,而且询问有50000个,


  • \begin{gather*}
    要求\ \ \ \ \ \ \ \ f( n) =\ \sum\nolimits ^{\ b}_{\ y=1} \ \ \sum\nolimits ^{\ a}_{\ x=1} \ [ \ gcd( x,y) =n\ ]\\
    \\
    则\ \ \ \ \ \ \ \ \ \ \ F( n) =\sum\nolimits ^{\ b}_{\ y=1}\sum\nolimits ^{\ a}_{\ x=1}\sum\nolimits _{\ n|gcd( x,y)} \ 1\\
    \\
    因为\ \ \ \ \ \ \ \sum\nolimits _{\ n|gcd( x,y)} 1=\sum\nolimits _{\ n|d} \ [ \ gcd( x,y) =d\ ]\\
    \\
    F( n) \ =\ \sum\nolimits _{\ n|d} \ \ f( d)\\
    \\
    然后就是莫比乌斯反演辣\\
    \\
    \end{gather*}

  • 代码:
  •  1 #include <bits/stdc++.h>
     2 #define nmax 50010
     3  
     4 using namespace std;
     5 int pri[nmax],table[nmax],mu[nmax];
     6 int cp=-1;
     7  
     8 void getmu(){
     9     for (int i=2; i<nmax; i++) {
    10         if(table[i]==0) { pri[++cp]=i; mu[i]=-1; }
    11         for (int j=0; j<=cp; j++) {
    12             int t=i*pri[j];
    13             if(t>=nmax) break;
    14             table[t]=1;
    15             if(i%pri[j]==0) {mu[t]=0; break;}
    16             mu[t]=mu[i]*(-1);
    17         }
    18     }
    19     mu[1]=1;
    20     for (int i=0; i<nmax; i++) mu[i]+=mu[i-1];
    21 }
    22  
    23 int main(){
    24     int n,a,b,d;
    25     getmu();
    26      cin>>n;
    27     while(n--){
    28         scanf("%d%d%d",&a,&b,&d);
    29         if(a>b) swap(a,b);
    30         a/=d;
    31         b/=d;
    32         //分块算答案
    33         int last,ans=0;
    34         for (int i=1; i<=a; i=last+1){
    35             last=min( a/(a/i) , b/(b/i) );
    36             ans+=(a/i)*(b/i)*(mu[last]-mu[i-1]);
    37         }
    38         printf("%d\n",ans);
    39     }
    40     return 0;
    41 }
    》w《

     

POJ3904 Sky Code

  • 给出一堆数,让你找有多少个四元组(a,b,c,d),满足a,b,c,d的gcd为1
  • 比较裸的莫比乌斯反演,跟上面一样
  • F(n) 为满足n|gcd(a,b,c,d)的四元组个数,f(n)为满足gcd(a,b,c,d)=1的四元组个数
  •  于是这样
    \begin{equation*}
    F( n) =\sum\limits _{n|d} f( d)
    \end{equation*}
  • 于是就可以根据公式反演了
  • 代码:
     1 #include <algorithm>
     2 #include <cstring>
     3 #include <iostream>
     4 #include <cstdio>
     5 #define nmax 10010
     6 
     7 using namespace std;
     8 typedef long long ll;
     9 int n,minx,cp=0;
    10 int t1[nmax]={0},in[nmax],mu[nmax],t3[nmax],pri[nmax];
    11 ll c[nmax][5]={0};
    12 ll ans;
    13 
    14 void getmu(){
    15     for (int i=2; i<nmax; i++) {
    16         if(t3[i]==0) { pri[++cp]=i; mu[i]=-1; }
    17         for (int j=1; j<=cp; j++) {
    18             int tx=i*pri[j];
    19             if(tx>nmax) break;
    20             t3[tx]=1;
    21             if(i%pri[j]==0) break;
    22             mu[tx]=mu[i]*(-1);
    23         }
    24     }
    25     mu[1]=1;
    26 }
    27 
    28 int main(){
    29     //组合数的表 c
    30     for (ll i=1; i<nmax; i++) c[i][1]=i;
    31     for (ll i=2; i<=4; i++) c[i][i]=1;
    32     for (ll j=2; j<=4; j++) for (ll i=j+1; i<nmax; i++) c[i][j]=c[i-1][j]+c[i-1][j-1];
    33     //莫比乌斯函数的表 mu
    34     getmu();
    35     while(scanf("%d",&n)!=EOF){
    36         memset(t1,0,sizeof(t1));
    37         ans=0;
    38         minx=nmax*10;
    39         for (int i=0; i<n; i++) { scanf("%d",&in[i]); minx=min(minx,in[i]); }
    40         //t1[i]有多少个数是i的倍数
    41         for (int i=0; i<n; i++) for (int j=1; j*j<=in[i]; j++) {
    42             if(in[i]%j==0) {
    43                 t1[j]++;
    44                 if( j != in[i]/j ) t1[in[i]/j]++;
    45             }
    46         }
    47 
    48         //反演部分
    49         for (int i=1; i<nmax; i++) ans+=mu[i]*c[ t1[i] ][4];
    50         printf("%lld\n",ans);
    51     }
    52     return 0;
    53 }
    呜呼~

     

posted @ 2019-09-26 19:45  连昵称都不能重复  阅读(192)  评论(0编辑  收藏  举报