HDU6434 Problem I. Count(欧拉函数)
题目大意
求
∑
i
=
1
n
∑
j
=
1
i
−
1
[
g
c
d
(
i
−
j
,
i
+
j
)
=
1
]
(
n
≤
2
e
7
)
\sum_{i=1}^n\sum_{j=1}^{i-1}[gcd(i-j,i+j)=1](n\le 2e7)
i=1∑nj=1∑i−1[gcd(i−j,i+j)=1](n≤2e7)
T(
T
≤
1
e
5
T\le 1e5
T≤1e5)组数据.
解题思路(误)
莫比乌斯反演
原式可以写作
∑
i
=
1
n
∑
j
=
1
n
−
i
[
g
c
d
(
i
,
i
+
2
j
)
=
1
]
=
∑
i
=
1
n
∑
j
=
1
n
−
i
∑
d
∣
i
,
2
∗
j
μ
[
d
]
\begin{aligned} &\sum_{i=1}^n\sum_{j=1}^{n-i}[gcd(i,i+2j)=1]\\ &=\sum_{i=1}^n\sum_{j=1}^{n-i}\sum_{d|i,2*j}\mu[d] \end{aligned}
i=1∑nj=1∑n−i[gcd(i,i+2j)=1]=i=1∑nj=1∑n−id∣i,2∗j∑μ[d]
由此,只需要通过整除分块即可得出答案
TLE代码
#include<bits/stdc++.h>
using namespace std;
const int size=2e7+5;
bool prime[size];
int p[size],mu[size];
int summu[size][2];
int tot;
void init()
{
mu[1]=1;
for(int i=1;i<size;i++) prime[i]=true;
for(int i=2;i<size;i++)
{
if(prime[i]){p[++tot]=i;mu[i]=-1;}
for(int j=1;j<=tot&&p[j]*i<size;j++)
{
prime[i*p[j]]=false;
if(i%p[j]==0)
{
mu[i*p[j]]=0;
break;
}
else mu[i*p[j]]=-mu[i];
}
}
summu[0][0]=summu[0][1]=0;
for(int i=1;i<size;i++)
{
summu[i][0]=summu[i-1][0];
summu[i][1]=summu[i-1][1];
summu[i][i%2]+=mu[i];
}
}
int main()
{
init();
int t;
scanf("%d",&t);
int n;
while(t--)
{
scanf("%d",&n);
long long ans=0;
for(int l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
int tp=n/l;
ans+=(summu[r][1]-summu[l-1][1])*(tp-1)*tp/2;
}
for(int l=2,r;l<=n;l=r+1)
{
r=2*n/(2*n/l);
int tp=2*n/l;
ans+=(summu[r][0]-summu[l-1][0])*(tp-2+tp%2)*(tp/2)/2;
}
printf("%lld\n",ans);
}
}
正确的解题思路
原式可以写作
∑
i
=
1
n
∑
j
=
1
i
−
1
[
g
c
d
(
j
,
2
i
−
j
)
=
1
]
\sum_{i=1}^n\sum_{j=1}^{i-1}[gcd(j,2i-j)=1]
i=1∑nj=1∑i−1[gcd(j,2i−j)=1]
显然有若j与2i互素,则j与2i-j也会互素,因此有
φ
[
2
i
]
/
2
=
∑
j
=
1
i
−
1
[
g
c
d
(
j
,
2
i
−
j
)
=
1
]
\varphi[2i]/2=\sum_{j=1}^{i-1}[gcd(j,2i-j)=1]
φ[2i]/2=∑j=1i−1[gcd(j,2i−j)=1]
AC代码
#include<bits/stdc++.h>
using namespace std;
const int size=2e7+5;
bool prime[size];
int p[size],phi[size],tot;
long long ans[size];
void init()
{
phi[1]=1;
for(int i=1;i<size;i++) prime[i]=true;
for(int i=2;i<size;i++)
{
if(prime[i])
{
p[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot&&p[j]*i<size;j++)
{
prime[i*p[j]]=false;
if(i%p[j]==0)
{
phi[i*p[j]]=phi[i]*p[j];
break;
}
else phi[i*p[j]]=phi[i]*phi[p[j]];
}
}
ans[0]=0;
for(int i=1;i<size;i++)
{
long long tmp=phi[i];
if(i%2==0) tmp*=2;
ans[i]=ans[i-1]+tmp/2;
}
}
int main()
{
int t;
init();
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
printf("%lld\n",ans[n]);
}
}