题目链接:https://nanti.jisuanke.com/t/A1956
题目大意:定义f[i]函数代表i=a * b的对数,其中a和b都不能是平方数的倍数,a * b与b * a不相同,t组样例,给出n,求1~n的f[i]之和。
思路:
整除分块:
1.我们可以用整除分块求出1-n的所有数的因子个数。那么就是所有的i=a*b的对数。然后我们考虑怎么把不满足条件的对数去掉。
2.i=a * b对于每个平方数的倍数x。他可以作为a或者b。x会对所有的x的倍数产生2个不合法的贡献。如果我们把所有的[1, 2e7]的x筛出来。用整除分块求不合法的贡献。
3.我们看2可以发现我减多了。例如36=4 * 9。4和9都减了一次贡献。那么就是说两个数都是平方数的倍数时,会减多。我们必须加回来。这个怎么算?我们还是考虑对于一个数x。他一共有n/x个倍数。分别是x, x * 2, x * 3, x * 4....x * (n/i),那么我们只要知道1-(n/i)有但是个平方数的倍数就知道这个数多减了多少贡献。当然也是用分块。
线性筛
我们打几个表就会发现:
1.i是素数: f[i]=2
2.f[i]是积性函数
2.假设i最小素因子是e。如果i%(e^k)==0并且k==2, f[i]/=4
3.假设i最小素因子是e。如果i%(e^k)==0并且k>=3, f[i]=0
既然f[i]是积性函数并且只和最小素因子有关,那我们用欧拉筛就可以搞了。
#include <bits/stdc++.h> #define LL long long using namespace std; int siz[20000005]; void init(){ for(int i=2; i*i<=20000000; i++){ //说明一定是i*i的因数已经筛过。那么所有的i*i的倍数一定被筛过了。 if(siz[i*i]) continue; for(int k=i*i; k<20000000; k+=i*i){ siz[k]=1; } } for(int i=2; i<=20000000; i++){ siz[i]+=siz[i-1]; } } LL cut(LL n){ LL ans=0; for(int l=1; l<=n; ){ int r=n/(n/l); ans+=1ll*(r-l+1)*(n/l);//求所有的贡献 ans-=2ll*(siz[r]-siz[l-1])*(n/l);//减去x是a或者b的贡献 ans+=1ll*(siz[r]-siz[l-1])*(siz[n/l]);//容斥把减多的加回来 l=r+1; } return ans; } int main(){ init(); int T; scanf("%d", &T); while(T--){ int n; scanf("%d", &n); printf("%lld\n", cut(n)); } return 0; } #include <bits/stdc++.h> #define LL long long using namespace std; LL siz[20000005], prime[20000005], tot; bool vis[20000005]; void init(int N){ tot=0; siz[1]=1; for(int i=2; i<=N; i++){ if(!vis[i]){ prime[tot++]=i; siz[i]=2; } for(int j=0; j<tot&&prime[j]*i<=N; j++){ siz[i*prime[j]]=siz[i]*siz[prime[j]];//积性函数 vis[i*prime[j]]=1; if(i%prime[j]==0){//假设i最小素因子是e。如果i%(e^k)==0并且k==2, f[i]/=4 siz[i*prime[j]]/=4; } if(i%(prime[j]*prime[j])==0){//假设i最小素因子是e。如果i%(e^k)==0并且k>=3, f[i]=0 siz[i*prime[j]]=0; } if(i%prime[j]==0){ break; } } } for(int i=1; i<=N; i++){ siz[i]+=siz[i-1]; } } int main(){ init(200); int T; scanf("%d", &T); while(T--){ int n; scanf("%d", &n); printf("%lld\n", siz[n]); } return 0; }