题目描述
传送门
题目大意: ∑ni=1∑ij=1μ(lcm(i,j)gcd(i,j))
题解
∑ni=1∑ij=1μ(lcm(i,j)gcd(i,j)
∑ni=1∑ij=1μ(i)∗μ(j)[gcd(i,j)=1]
∑nd=1μ(d)∑d|iμ(i)∑d|jμ(j)
∑nd=1μ(d)∑d|iμ(i)∑⌊id⌋j=1μ(dj)
另
f(d)
表示到当前枚举到的d为止,
∑⌊id⌋j=1μ(dj)
的和。
这个东西可以动态的维护,每次加入一个数,如果这个数有贡献,那么他一定是几个质数一次方的乘积。那么我们可以dfs得到他所有的约数,然后用
μ(i)
更新
f(d)
,同时也可以顺便计算出
∑d|iμ(i)∑⌊id⌋j=1μ(dj)
代码
#include<cstdio>
#include<algorithm>
#define N 10000000
using namespace std;
int T,pd[N+10],prime[N+10],mu[N+10],ans[1003],n,a[N],p[N+10];
int cnt,st[103],f[N+10],sum[N+10];
void init()
{
mu[1]=1;
for (int i=2;i<=n;i++) {
if (!pd[i]) {
prime[++prime[0]]=i;
p[i]=i;
mu[i]=-1;
}
for (int j=1;j<=prime[0];j++){
if (i*prime[j]>n) break;
pd[i*prime[j]]=1; p[i*prime[j]]=prime[j];
if (i%prime[j]==0) break;
mu[i*prime[j]]=-mu[i];
}
}
}
int dfs(int num,int x,int v)
{
if (num==cnt+1) {
f[x]+=v;
return mu[x]*f[x];
}
return dfs(num+1,x*st[num],v)+dfs(num+1,x,v);
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d",&T);
for (int i=1;i<=T;i++) scanf("%d",&a[i]),n=max(n,a[i]);
init();
for (int i=1;i<=n;i++) {
sum[i]=sum[i-1];
if (!mu[i]) continue;
cnt=0; int x=i;
for (;x>1;x/=p[x])
st[++cnt]=p[x];
sum[i]+=mu[i]*dfs(1,1,mu[i]);
}
for (int i=1;i<=T;i++) printf("%d\n",sum[a[i]]);
}