ACM-ICPC 2018 南京賽區網路預賽 J.SUM(線篩)
阿新 • • 發佈:2019-01-05
連結
題意:給出一個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;
}