求1-n之间Square-free integers的个数。
第一种方法是裸的容斥原理,枚举每一个质因子要还是不要,然后用总数减去存在平方质因子的数的个数。
剪枝的方法是在dfs内进行循环,这样避免每次都去递归试探一个质因子不要的情况。
代码基本是抄的,剪枝很强力。
#include<cstdio> #include<iostream> using namespace std; typedef long long ll; const int X=10000100; bool su[X]; long long s[X/10],top; void make(){ ll i,j; for(i=2;i<X;i++)if(!su[i]){ for(j=i*i;j<X;j+=i)su[j]=1; s[top++]=i*i; } } ll as,n; ll dfs(int id,ll d){ ll sum=0; while(id<top&&s[id]<=n/d ){ sum += n/(d*s[id])-dfs(id+1,d*s[id]); id++; } return sum; } int main(){ make(); int i,j,cs; for(scanf("%d",&cs);cs--;){ as=0; scanf("%lld",&n); printf("%lld\n",n-dfs(0,1)); } return 0; }
把每一个数写成p^2*q的形式,其中p是最大的能构成平方的因子,这样表示使得每一个数都是唯一的。
假设p=2,那么考虑1-n内有多少个可以写成2^2*q的数,是n/2/2个,
这其中也包括p!=2的情况,但是可以通过容斥把这些情况减去。
而加减的系数是通过mobius函数决定的,
于是通过枚举p,最后的答案是sum(n/p/p*mobius[p]);
枚举p的复杂度是O(n),求mobius的复杂度也有O(n)的算法。
代码如下。
#include<cstdio> const int X=10000010; bool su[X]; int u[X],s[X],top; void make(){ long long i,j; u[1]=1; for(i=2;i<X;i++){ if(!su[i]){ s[top++]=i; u[i]=-1; } for(j=0;j<top&&i*s[j]<X;j++){ su[i*s[j]]=1; if(i%s[j])u[i*s[j]]=u[i]*u[s[j]]; else break; } } } int main(){ make(); int i,cs; long long n,sum; for(scanf("%d",&cs);cs--;){ sum=0; scanf("%lld",&n); for(i=1;i<=n/i;i++) if(u[i]) sum+=n/i/i*u[i]; printf("%lld\n",sum); } return 0; }