1. 程式人生 > >ACM-ICPC 2018 南京賽區網路預賽 J.SUM(線篩)

ACM-ICPC 2018 南京賽區網路預賽 J.SUM(線篩)

連結
題意:給出一個f函式,定義為 把i拆分成兩個因子的方案數(且每一個因子都不能被平方數整除),求i=1nf(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; }