题目链接
显然,我们要找到 (i),(j),使 (gcd(i,j)=1),构造出的方案为 ((i^2+ij,j^2+ij,ij)) 和 ((j^2+ij,i^2+ij,ij))。
这里我们令 (i geq j),则方案个数为
(sumlimits_{i=1}^n{sumlimits_{j=1}^i{[gcd(i,j)=1][i^2+ij leq n]}})
对于 (i^2+ij leq n),我们求出 (j leq frac{n}{i}-i),再解 (frac{n}{2}-i geq 0),得 (i leq sqrt{n}),把式子改一下:
(sumlimits_{i=1}^{sqrt{n}}{sumlimits_{j=1}^{min (frac{n}{i}-i,i)}{[gcd(i,j)=1]}})
好哒,我们再看一看 (frac{n}{i}-i=i),解得 (i=sqrt{frac{n}{2}}),分情况讨论:
当 (i leq sqrt{frac{n}{2}}) 时,就等价于统计 (sumlimits_{j=1}^{i}{[gcd(i,j)=1]}),线性筛 (phi) 即可。
当 (sqrt{frac{n}{2}} < i leq sqrt{n}) 时,(j leq i),这个时候怎么办呢?
我们暴力容斥就好啦,线性筛时顺便筛出每个数的最小质因子,分解,设有 (K) 个不同的质因子,则 (j) 内与 (i) 互质的数有:
(j-sumlimits_{k=1}^{K}{(-1)^k*f(k)}),(f(k)=sumlimits_{a_1<a_2< cdots <a_k leq K}{frac{j}{prodlimits_{i=1}^{k}{p_{a_i}}}})
最后把输出 (2ans-1),再加上两个剪枝,就可以轻松跑过啦~
(frak{code:})
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e6+3;
int vis[N],pri[N],mu[N],a[N],now,num;
LL n,m,res,Max,ans,phi[N];
void init(int n){ //线性筛~
phi[1]=mu[1]=1;
for(int i=2;i<=n;++i){
if(!vis[i]) vis[i]=i,pri[++pri[0]]=i,phi[i]=i-1,mu[i]=-1;
for(int j=1,k;(k=i*pri[j])<=n;++j){
vis[k]=pri[j];
if(i%pri[j]) mu[k]=-mu[i],phi[k]=phi[i]*phi[pri[j]];
else{phi[k]=phi[i]*pri[j];break;}
}
}
}
void calc(int n){ //分解质因数~
a[0]=0;
while(n^1){
if(vis[n]^a[a[0]]) a[++a[0]]=vis[n];
n/=vis[n];
}
}
void dfs(int k,int kk,int val,int op){ //搜索组合方案~
if(kk==num){ans+=op*(now/val);return;}
if(a[0]-k+1<num-kk) return; //显而易见的剪枝1~
dfs(k+1,kk+1,val*a[k],op);
dfs(k+1,kk,val,op);
}
int main()
{
scanf("%lld",&n);init(1e6);
m=sqrt(1.0*n/2),Max=sqrt(n);
for(int i=1;i<=m;++i) ans+=phi[i];
for(int i=m+1,k;i<=Max;++i){
now=n/i-i;int res=ans;
calc(i),ans+=now;
for(num=1,k=a[num];num<=a[0];++num,k*=a[num]){
if(k>now) break; //显而易见的剪枝2,如果最小的num个质因子相乘大于now,则之后的贡献都为0~
dfs(1,0,1,num&1?-1:1);
}
}
cout<<ans*2-1<<endl; //(2,2,1)会被算2次,所以减1~
return 0;
}