zoukankan      html  css  js  c++  java
  • 洛谷 P2398 GCD SUM || uva11417,uva11426,uva11424,洛谷P1390,洛谷P2257,洛谷P2568

    https://www.luogu.org/problemnew/show/P2398

    $原式=sum_{k=1}^n(ksum_{i=1}^nsum_{j=1}^n[(i,j)=k])$

    方法1:

    发现暴力枚举k,就变成这道模板题

    复杂度O(nlogn)

     1 #pragma GCC optimize("Ofast")
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<vector>
     6 using namespace std;
     7 #define fi first
     8 #define se second
     9 #define mp make_pair
    10 #define pb push_back
    11 typedef long long ll;
    12 typedef unsigned long long ull;
    13 typedef pair<int,int> pii;
    14 #define N 2000000
    15 ll prime[N+100],len,mu[N+100];
    16 bool nprime[N+100];
    17 ll n,ans,a2;
    18 ll F(ll x)    {return (n/x)*(n/x);}
    19 int main()
    20 {
    21     ll i,j,k;
    22     mu[1]=1;
    23     for(i=2;i<=N;i++)
    24     {
    25         if(!nprime[i])    prime[++len]=i,mu[i]=-1;
    26         for(j=1;j<=len&&i*prime[j]<=N;j++)
    27         {
    28             nprime[i*prime[j]]=1;
    29             if(i%prime[j]==0)    {mu[i*prime[j]]=0;break;}
    30             else    mu[i*prime[j]]=-mu[i];
    31         }
    32     }
    33     scanf("%lld",&n);
    34     for(k=1;k<=n;k++)
    35     {
    36         a2=0;
    37         for(i=1;i<=n/k;i++)
    38             a2+=mu[i]*F(i*k);
    39         ans+=a2*k;
    40     }
    41     printf("%lld",ans);
    42     return 0;
    43 }
    View Code

    方法2:

    https://www.luogu.org/blog/Cinema/solution-p1390

    (这是一道类似的题的题解)

    复杂度好像是O(n)

    具体地说,是嵌套整除分块,第一重枚举n/d,m/d,第二重枚举(n/d)/x,(m/d)/x


    方法3:

    $sum_{i=1}^nsum_{j=1}^n[(i,j)=k]=sum_{i=1}^{{lfloor}n/k{ floor}}sum_{j=1}^{{lfloor}n/k{ floor}}[(i,j)=1]$

    $=2*(sum_{i=1}^{{lfloor}n/k{ floor}}sum_{j=1}^{i}[(i,j)=1])-1=2*(sum_{i=1}^{{lfloor}n/k{ floor}}varphi(i))-1$

    这个欧拉函数的前缀和可以先预处理出来,然后枚举k

    复杂度O(n)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<vector>
     5 using namespace std;
     6 #define fi first
     7 #define se second
     8 #define mp make_pair
     9 #define pb push_back
    10 typedef long long ll;
    11 typedef unsigned long long ull;
    12 typedef pair<int,int> pii;
    13 #define N 2000000
    14 ll prime[N+100],len,phi[N+100];
    15 ll d[N+100];
    16 bool nprime[N+100];
    17 ll n,ans;
    18 ll F(ll x)    {return (n/x)*(n/x);}
    19 int main()
    20 {
    21     ll i,j,k;
    22     phi[1]=1;
    23     for(i=2;i<=N;i++)
    24     {
    25         if(!nprime[i])    prime[++len]=i,phi[i]=i-1;
    26         for(j=1;j<=len&&i*prime[j]<=N;j++)
    27         {
    28             nprime[i*prime[j]]=1;
    29             if(i%prime[j]==0)    {phi[i*prime[j]]=phi[i]*prime[j];break;}
    30             else    phi[i*prime[j]]=phi[i]*(prime[j]-1);
    31         }
    32     }
    33     for(i=1;i<=N;i++)    d[i]+=d[i-1]+phi[i];
    34     scanf("%lld",&n);
    35     for(k=1;k<=n;k++)    ans+=k*(2*d[n/k]-1);
    36     printf("%lld",ans);
    37     return 0;
    38 }
    View Code

    方法4:

    注意到方法3第35行的可以用整除分块优化,甚至可以O(n)预处理后每次O(sqrt(n))回答


    类似的题(题面并不完全一样)

    https://www.luogu.org/problemnew/show/UVA11417

    https://www.luogu.org/problemnew/show/UVA11426

    https://www.luogu.org/problemnew/show/P1390


    https://www.luogu.org/problemnew/show/UVA11424

    这题做法稍微有点不一样,因为数据组数多,不能直接每次O(n)回答

    可以用方法4水过去

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<vector>
     5 using namespace std;
     6 #define fi first
     7 #define se second
     8 #define mp make_pair
     9 #define pb push_back
    10 typedef long long ll;
    11 typedef unsigned long long ull;
    12 typedef pair<int,int> pii;
    13 #define N 2000000
    14 ll prime[N+100],len,phi[N+100];
    15 ll d[N+100];
    16 bool nprime[N+100];
    17 ll n,ans;
    18 ll F(ll x)    {return (n/x)*(n/x);}
    19 int main()
    20 {
    21     ll i,j,k;
    22     phi[1]=1;
    23     for(i=2;i<=N;i++)
    24     {
    25         if(!nprime[i])    prime[++len]=i,phi[i]=i-1;
    26         for(j=1;j<=len&&i*prime[j]<=N;j++)
    27         {
    28             nprime[i*prime[j]]=1;
    29             if(i%prime[j]==0)    {phi[i*prime[j]]=phi[i]*prime[j];break;}
    30             else    phi[i*prime[j]]=phi[i]*(prime[j]-1);
    31         }
    32     }
    33     for(i=1;i<=N;i++)    d[i]+=d[i-1]+phi[i];
    34     while(1){
    35     scanf("%lld",&n);
    36     if(n==0)    break;
    37     ans=0;
    38     for(i=1;i<=n;i=j+1)
    39     {
    40         j=min(n,n/(n/i));
    41         ans+=(i+j)*(j-i+1)/2*(2*d[n/i]-1);
    42     }
    43     printf("%lld
    ",(ans-(n+1)*n/2)/2);}
    44     return 0;
    45 }
    View Code

    或者:

    设sum[x]=$sum_{i=1}^{x-1}(i,x)$

    那么询问为n时,答案就是$sum_{i=1}^n{sum[i]}$

    而sum[x]=$sum_{k=1}^{x-1}(k*sum_{i=1}^{x-1}[(i,x)=k])$

    显然只有当k是x的因子且k不等于x时才能有贡献

    枚举k,$sum_{i=1}^{x-1}[(i,x)=k]=sum_{i=1}^{frac{x}{k}-1}[(i,frac{x}{k})=1]$

    $=(sum_{i=1}^{frac{x}{k}}[(i,frac{x}{k})=1])=phi(frac{x}{k})$

    可以看出来sum[x]=$(sum_{d|x且d{ eq}x}(d*phi(frac{x}{d})))$

    实际实现时可以把枚举因子变为枚举倍数

    复杂度所有数据加起来O(nlogn+q)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<vector>
     5 using namespace std;
     6 #define fi first
     7 #define se second
     8 #define mp make_pair
     9 #define pb push_back
    10 typedef long long ll;
    11 typedef unsigned long long ull;
    12 typedef pair<int,int> pii;
    13 #define N 200000
    14 ll prime[N+100],len,phi[N+100];
    15 ll sum[N+100];
    16 bool nprime[N+100];
    17 ll n,ans[N+100];
    18 int main()
    19 {
    20     ll i,j,k;
    21     phi[1]=1;
    22     for(i=2;i<=N;i++)
    23     {
    24         if(!nprime[i])    prime[++len]=i,phi[i]=i-1;
    25         for(j=1;j<=len&&i*prime[j]<=N;j++)
    26         {
    27             nprime[i*prime[j]]=1;
    28             if(i%prime[j]==0)    {phi[i*prime[j]]=phi[i]*prime[j];break;}
    29             else    phi[i*prime[j]]=phi[i]*(prime[j]-1);
    30         }
    31     }
    32     for(i=1;i<=N;i++)
    33         for(j=2*i;j<=N;j+=i)
    34             sum[j]+=i*phi[j/i];
    35     for(i=1;i<=N;i++)    ans[i]=ans[i-1]+sum[i];
    36     while(1){
    37     scanf("%lld",&n);
    38     if(n==0)    break;
    39     printf("%lld
    ",ans[n]);
    40     }
    41     return 0;
    42 }
    View Code

    https://www.luogu.org/problemnew/show/P2257

    事实上此题做法与以上几题是一样的,如果数据范围一样。。

    然而此题有多组数据又是1e7,需要更好的做法

    不妨设n<=m

    根据方法2,得到答案是$sum_{1<=k<=n且k为质数}sum_{i=1}^nmu(i){lfloor}frac{n}{ik}{ floor}{lfloor}frac{m}{ik}{ floor}$

    好像不能化简了?然而A不了题?

    强行解释一波推算方法:注意到最终的可能求法是整除分块,要枚举ik的积,那么试着把它提出来

    第二种解释方法:为什么要这么写式子?学莫比乌斯反演时学来的式子枚举的就是d啊

    看了题解,发现可以令d=ik

    答案就等于$sum_{1<=k<=n且k为质数}sum_{k|d}mu(frac{d}{k}){lfloor}frac{n}{d}{ floor}{lfloor}frac{m}{d}{ floor}$

    $=sum_{d=1}^nsum_{k|d且k为质数}mu(frac{d}{k}){lfloor}frac{n}{d}{ floor}{lfloor}frac{m}{d}{ floor}$

    $sum_{k|d且k为质数}mu(frac{d}{k})$可以预处理了,根据那个质数个数(近似)公式,预处理复杂度大概接近O(n)了。。

    可以A了。。。

     1 //#pragma GCC optimize("Ofast")
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<vector>
     6 using namespace std;
     7 #define fi first
     8 #define se second
     9 #define mp make_pair
    10 #define pb push_back
    11 typedef long long ll;
    12 typedef unsigned long long ull;
    13 typedef pair<int,int> pii;
    14 #define N 10000000
    15 int prime[N+100],len,mu[N+100],dd[N+100];
    16 bool nprime[N+100];
    17 int n,m;ll ans;
    18 int main()
    19 {
    20     int i,j,k,T,TT;
    21     mu[1]=1;
    22     for(i=2;i<=N;i++)
    23     {
    24         if(!nprime[i])    prime[++len]=i,mu[i]=-1;
    25         for(j=1;j<=len&&i*prime[j]<=N;j++)
    26         {
    27             nprime[i*prime[j]]=1;
    28             if(i%prime[j]==0)    {mu[i*prime[j]]=0;break;}
    29             else    mu[i*prime[j]]=-mu[i];
    30         }
    31     }
    32     for(i=1;i<=len;i++)
    33         for(j=1,k=prime[i];j<=N/prime[i];j++,k+=prime[i])
    34             dd[k]+=mu[j];
    35     for(i=1;i<=N;i++)    dd[i]+=dd[i-1];
    36     scanf("%d",&T);
    37     for(TT=1;TT<=T;TT++)
    38     {
    39         scanf("%d%d",&n,&m);
    40         if(n>m)    swap(n,m);
    41         ans=0;
    42         for(i=1;i<=n;i=j+1)
    43         {
    44             j=min(n,min(n/(n/i),m/(m/i)));
    45             ans+=ll(dd[j]-dd[i-1])*(n/i)*(m/i);
    46         }
    47         printf("%lld
    ",ans);
    48     }
    49     return 0;
    50 }
    View Code

    双倍经验https://www.luogu.org/problemnew/show/P2568

    好吧以上这题时限有点紧。。不过单次询问O(n)也可以用方法3或者方法2

  • 相关阅读:
    C# 设计模式-桥接模式
    C# 设计模式-外观模式
    C# 设计模式-代理模式
    楼层导航奇葩问题解决
    楼层导航和回顶部
    回顾
    禁止右击选中
    安装客服在线系统
    csdn 分享私藏的18个黑科技网站,想找什么软件就找什么软件!!!
    eyoucms 模板
  • 原文地址:https://www.cnblogs.com/hehe54321/p/9315244.html
Copyright © 2011-2022 走看看