链接
题意:给出一个f函数,定义为 把i拆分成两个因子的方案数(且每一个因子都不能被平方数整除),求
∑ni=1f(i)
∑
i
=
1
n
f
(
i
)
分析:首先我们考虑f(i)如何求
对于任意一个i,我们可以拆分成素因子的乘积形式
此时就可以根据素因子的幂分为3种情况讨论
1.素因子的幂>=3时,由于只能拆分成两个因子无论怎么组合,f(i)一定为0
2.素因子的幂=2,此时只能将这个素因子两边各放一个
3.素因子的幂=1,此时既将这个素因子 可以放左边的因子,也可以放右边因子,对f贡献*2
f知道怎么求了,观察数据规模n<=2e7,那么肯定是要线性解决,由于还要求素因子,于是想到线性筛法,那么我们能否改造一下线筛,让它可以在筛的时候把f也计算出来
观察线筛,发现,每次他都用 i*prime[j]去筛素数
假设 此时prime[j]在i*prime[j]的因子中只出现了一次,那么f[i*prime[j]]=f[i]*2
如果 prime[i]在他的因子中出现了多次呢?如果出现多次,那么此时i%prime[j]一定是等于0的,这时候我们就把 f[i*prime[j]]/=4,因为一个prime[j]会让f变为原来的2倍,而i里又包含了一个prime[j],所以要除4
这时候只算对了2和3的情况,对于1情况,可以暴力搞一下,3次方的话复杂度也不大
还有代码里的 f初值赋为-1仅仅是因为meset不能赋值为1,没有别的意图。。。
#include <bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;
const int M=2e7+200;
int prime[M/10],tot,f[M];
bool isprime[M];
void init()
{
memset(f,-1,sizeof(f));
f[1]=1;
isprime[1]=1;
for(int i=2; i<M; i++)
{
if(!isprime[i])
{
prime[tot++]=i;
f[i]=-2;
}
for(int j=0; j<tot&&i*prime[j]<M; j++)
{
isprime[i*prime[j]]=1;
f[i*prime[j]]=f[i]*2;
if(i%prime[j]==0)f[i*prime[j]]/=4;
if(i%prime[j]==0)
break;
}
}
for(ll i=2;i*i*i<M;i++)
{
ll x=i*i*i;
for(int j=2;j*x<M;j++)
f[j*x]=0;
}
for(int i=2;i<M;i++)
f[i]=abs(f[i])+abs(f[i-1]);
}
int main()
{
init();
int t,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
printf("%d\n",f[n]);
}
return 0;
}