洛谷 P2398 GCD SUM || uva11417,uva11426,uva11424,洛谷P1390,洛谷P2257,洛谷P2568

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

$原式=\sum_{k=1}^n(k\sum_{i=1}^n\sum_{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}^n\sum_{j=1}^n[(i,j)=k]=\sum_{i=1}^{{\lfloor}n/k{\rfloor}}\sum_{j=1}^{{\lfloor}n/k{\rfloor}}[(i,j)=1]$

$=2*(\sum_{i=1}^{{\lfloor}n/k{\rfloor}}\sum_{j=1}^{i}[(i,j)=1])-1=2*(\sum_{i=1}^{{\lfloor}n/k{\rfloor}}\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\n",(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{\neq}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\n",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}^n\mu(i){\lfloor}\frac{n}{ik}{\rfloor}{\lfloor}\frac{m}{ik}{\rfloor}$

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

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

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

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

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

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

$\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\n",ans);
48     }
49     return 0;
50 }
View Code

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

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

 

posted @ 2018-07-16 09:29  hehe_54321  阅读(231)  评论(0编辑  收藏  举报
AmazingCounters.com