ACM-ICPC 2018 南京赛区网络预赛 J.SUM(线筛)

链接
题意:给出一个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 
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; iif(!isprime[i])
        {
            prime[tot++]=i;
            f[i]=-2;
        }
        for(int j=0; j*prime[j]*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*ix=i*i*i;
        for(int j=2;j*x*x]=0;
    }
    for(int i=2;iabs(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;
}

你可能感兴趣的:(【思路题】,【数论】)