zoukankan      html  css  js  c++  java
  • bzoj 2818 Gcd(欧拉函数 | 莫比乌斯反演)

    【题目链接】

        http://www.lydsy.com/JudgeOnline/problem.php?id=2818

    【题意】

      问(x,y)为质数的有序点对的数目。

    【思路一】

      定义f[i]表示i之前(x,y)=1的有序点对的数目,则有递推式:

              f[1]=1

              f[i]=f[i-1]+phi[i]*2

      我们依次枚举小于n的所有素数,对于素数t,(x,y)=t的数目等于(x/t,y/t),即f[n/t]。

    【代码一】

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 using namespace std;
     5 
     6 typedef long long ll;
     7 const int N = 1e7+10;
     8 
     9 int su[N],tot,phi[N];
    10 ll f[N];
    11 
    12 void get_pre(int n)
    13 {
    14     phi[1]=1;
    15     for(int i=2;i<=n;i++) if(!phi[i]) {
    16         su[++tot]=i;
    17         for(int j=i;j<=n;j+=i) {
    18             if(!phi[j]) phi[j]=j;
    19             phi[j]=phi[j]/i*(i-1);
    20         }
    21     }
    22     f[1]=1;
    23     for(int i=2;i<=n;i++) f[i]=f[i-1]+2*phi[i];
    24 }
    25 
    26 int n;
    27 
    28 int main()
    29 {
    30     scanf("%d",&n);
    31     get_pre(n);
    32     ll ans=0;
    33     for(int i=1;i<=tot;i++)
    34         ans+=f[n/su[i]];
    35     printf("%lld
    ",ans);
    36     return 0;
    37 }

    【思路二】

      其它思路一样,不同的是使用莫比乌斯反演计算(x,y)=1的数目,累计答案的时间复杂度为O(n sqrt(n))

      推倒过程:

    【代码二】

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 using namespace std;
     5 
     6 typedef long long ll;
     7 const int N = 1e7+10;
     8 
     9 int su[N],mu[N],tot,vis[N];
    10 
    11 void get_mu(int n)
    12 {
    13     mu[1]=1;
    14     for(int i=2;i<=n;i++) {
    15         if(!vis[i]) {
    16             su[++tot]=i;
    17             mu[i]=-1;
    18         }
    19         for(int j=1;j<=tot&&su[j]*i<=n;j++) {
    20             vis[i*su[j]]=1;
    21             if(i%su[j]==0) mu[i*su[j]]=0;
    22             else mu[i*su[j]]=-mu[i];
    23         }
    24     }
    25     for(int i=1;i<=n;i++) mu[i]+=mu[i-1];
    26 }
    27 
    28 int n;
    29 
    30 ll calc(int n)
    31 {
    32     int i,last; ll ans=0;
    33     for(int i=1;i<=n;i=last+1) {
    34         last=n/(n/i);
    35         ans+=(ll)(n/i)*(n/i)*(mu[last]-mu[i-1]);
    36     }
    37     return ans;
    38 }
    39 
    40 int main()
    41 {
    42 //    freopen("in.in","r",stdin);
    43 //    freopen("out.out","w",stdout);
    44     scanf("%d",&n);
    45     get_mu(n);
    46     ll ans=0;
    47     for(int i=1;i<=tot;i++)
    48         ans+=calc(n/su[i]);
    49     printf("%lld
    ",ans);
    50     return 0;
    51 }

    UPD.16/4/8

      另外莫比乌斯反演还有一种O(n)预处理O(sqrt(n))查询的做法 click Here

  • 相关阅读:
    辅助工具链接
    参考资料链接
    oracle sql 查询前十条数据
    oracle sql 按照汉字规则排序
    oracle sql 修改timestamp数据
    eclipse闪退
    js 数组Array
    面试题:树的子结构
    面试题:二叉树中和为某一路径
    面试题:二叉搜索树的后序遍历
  • 原文地址:https://www.cnblogs.com/lidaxin/p/5337479.html
Copyright © 2011-2022 走看看