1. 程式人生 > >【NOIP入門數論】分解質因數

【NOIP入門數論】分解質因數

自創題目詳解

資料包

題面

【題目名稱】分解質因數
【時間限制】1000ms
【空間限制】128M
【題目描述】
Pii.
n滿(a,b)
(1)1abn;
(2)ta,b,PtPn.
【輸入格式】
一個正整數n.
【輸出格式】
一個正整數,表示合題意的有序正整數對的數目.

樣例輸入1 樣例說明1
6 在滿足1<=a<=b<=6的條件下,共有21對;除開(5,5)不合題意,其餘均滿足條件。
樣例輸出1
20
樣例輸入2 樣例說明2
7 在滿足1<=a<=b<=7的條件下,共有28對;其中(2,2),(2,4),(2,6),(3,3),(3,6),
樣例輸出2 (4,4),(4,6),(5,5),(6,6)不合題意.
19

【資料範圍】
50%1n1000;
100%1n1000000.
————————————————————————————————————————————————
【題解】
50分演算法:
只要聽話,就一定可以拿全50分!我們只需要先將n分解質因數,並將這些質因數都記錄下來。
之後老老實實列舉a,b再用歐幾里得求出最大公約數,直接分解質因數驗證即可。
時間複雜度O

((n2)logn)其實這個log n相當的小,1000ms的時限是可以過完50分的資料的。
下面給出50分的暴力程式碼:

#include<cstdio>  
#define MAXN 101000  
int gcd(int a,int b)  
{  
    int t;  
    while(b>0)  
    {  
        t=a;  
        a=b;  
        b=t%b;  
    }  
    return a;  
}//非遞迴輾轉相除法;  
bool P[MAXN];  
int n;  
bool ok(int x)  
{  
    for
(int i=2;i*i<=x;i++) { if(x%i==0) { if(!P[i]) { return false; } } while(x%i==0) { x/=i; } } if(x>1&&P[x]==0)//如果x仍大於1,說明x自身是個質數,以至於在x^0.5範圍內找不到x的因數; { return false; } return true; }//判斷是否為子集 int main() { freopen("prime.in","r",stdin); freopen("prime.out","w",stdout); scanf("%d",&n); int k=n; for(int i=2;i*i<=n;i++) { if(k%i==0) { P[i]=1; } while(k%i==0) { k/=i; } }//分解n; if(k>1) { P[k]=1; } //原理見ok函式 long long cnt=0LL; for(int i=1;i<=n;i++) { for(int j=i;j<=n;j++) { if(ok(gcd(i,j))) { cnt+=1LL; } } } printf("%I64d",cnt); return 0; }

100分演算法:
可以看出資料範圍是相當的大,列舉a,b是不可行的。我們不妨思考一下一個比較基礎的問題:
如何求解1~n中有多少個數與n互質?答案是用尤拉函式。而尤拉函式的原理即是將n含有的質因數逐一篩去。
因為我們要求解的數是一定不含這些質因數的,所以將這些質數篩去是合理的。
而現在的問題是,我們要求解有多少個小於i的數,與i的公共質因數落在一個確定的集合中。
在求尤拉函式時,由於不能有質數,所以我們篩去質數;而這裡,P_n 中的質數是可以出現的,於是我們就沒有必要將它們篩去。也就是說,計算phi[i]=i(11p1)(11p2)(11pk)時,凡是在集合P_n中的質數,就不乘以(11p),只將那些不在集合Pi中的質數篩掉即可。
甚至,我們可以有一個更大膽的想法:對於P_n中的質數,我們並不將其視為質數。這樣題設條件就是一種變相的互質了!!!
於是當我們求出做了手腳的尤拉函式phi[i]後,ni=1phi[i]就是最終答案!
phi[i]的求法,最優秀的是用線性篩,但是沒有這個必要。標程採用的O(n*log n)的演算法,跑得飛快!
為了方便,標程的做法是並不在求尤拉函式時做判斷,而是照常求出尤拉函式,但在求和時,將原本不該篩而現在多篩了的部分補償回來。
下面放100分程式碼:老實說,比暴力要短。

#include<cstdio>  
#define MAXN 2000100  
int P[MAXN],tot=0;  
int n,f[MAXN];  
int main()  
{  
    freopen("prime.in","r",stdin);  
    freopen("prime.out","w",stdout);  
    scanf("%d",&n);  
    int k=n;  
    for(int i=2;i*i<=k;i++)  
    {  
        if(k%i==0)  
        {  
            P[++tot]=i;  
        }  
        while(k%i==0)  
        {  
            k/=i;  
        }  
    }  
    if(k>1)  
    {  
        P[++tot]=k;  
    }  
    for(int i=1;i<=n;i++)  
    {  
        f[i]=i;  
    }  
    for(int i=2,j;i<=n;i++)  
    {  
        if(f[i]==i)  
        {  
            for(j=i;j<=n;j+=i)  
            {  
                f[j]-=f[j]/i;  
            }  
        }  
    }//O(n*log n)  
    long long ans=0;  
    for(int i=1;i<=n;i++)  
    {  
        for(int p=1;p<=tot;p++)  
        {  
            if(i%P[p]==0)  
            {  
                f[i]+=f[i]/(P[p]-1);  
            }  
        }//tot不會超過7,所以效率相當高!  
        ans+=(long long)f[i];  
    }  
    printf("%I64d",ans);  
    fclose(stdin);  
    fclose(stdout);  
    return 0;  
}  

此題還有其他100分解法,這裡簡要提一下:(下面用gcd(a,b)表示a,b的最大公約數)
對於gcd(a,b)=t,有gcd(a/t,b/t)=1,於是(a,b)的數目可以藉助phi[b/t]來求解。但是要預處理出所有合題意的t.
此解法比標程解法要麻煩許多!!!
【總結】
此題的關鍵是要對尤拉函式的原理和本質有清楚的認識,這樣才可能用改造後的尤拉函式來求解問題。

以上是本人NOIP前發的題解,現在看來相當的naive.
之前提到線性篩尤拉函式,但是後面的補償部分仍然不是線性的。或者說是一個有常數的線性演算法,常數取決於n的質因數集合的size.
其實這樣的演算法效率上已經很接近線性了,不過這裡我提一下正經的線性做法:
考慮改造後的尤拉函式,記作g(x).它仍然是一個積性函式.於是就可以用線性篩直接處理出g(x).
具體來說,先用O(n)的演算法分解n的質因數,(當然你會用O(n14)的演算法更好),然後線性篩.
凡是遇到質數x,如果在P_n集合中,g(x)=x;否則,g(x)=x-1;對於合數,利用積性函式的性質求解就好.
還是放一段程式碼吧:

#include<cstdio>  
#define MAXN 2000100  
int P[MAXN],tot=0;  
int n,f[MAXN];  
int main()  
{  
    freopen("prime.in","r",stdin);  
    freopen("prime.out","w",stdout);  
    scanf("%d",&n);  
    int k=n;  
    for(int i=2;i*i<=k;i++)  
    {  
        if(k%i==0)  
        {  
            P[++tot]=i;  
        }  
        while(k%i==0)  
        {  
            k/=i;  
        }  
    }  
    if(k>1)  
    {  
        P[++tot]=k;  
    }  
    for(int i=1;i<=n;i++)  
    {  
        f[i]=i;  
    }  
    for(int i=2,j;i<=n;i++)  
    {  
        if(f[i]==i)  
        {  
            for(j=i;j<=n;j+=i)  
            {  
                f[j]-=f[j]/i;  
            }  
        }  
    }//O(n*log n)  
    long long ans=0;  
    for(int i=1;i<=n;i++)  
    {  
        for(int p=1;p<=tot;p++)  
        {  
            if(i%P[p]==0)  
            {  
                f[i]+=f[i]/(P[p]-1);  
            }  
        }//tot不會超過7,所以效率相當高!  
        ans+=(long long)f[i];  
    }  
    printf("%I64d",ans);  
    fclose(stdin);  
    fclose(stdout);  
    return 0;  
}  

相關推薦

NOIP入門數論分解質因數

自創題目詳解 資料包 題面 【題目名稱】分解質因數 【時間限制】1000ms 【空間限制】128M 【題目描述】 記Pi表示正整數i的質因數集合. 已知正整數n,求滿足下列條件的

NOIP校內模擬T1 一串數字(思考題+一點點數論知識)

關鍵是那些兩個數相乘拼成了立方數的 一個數分解質因數後,指數%3是不影響的 我們可以這樣想——我們通過一個數,反推出能夠和他湊成立方數的數 但有可能這個數在原序列裡不存在 不過沒關係 我們開兩個桶 每次比較下大小 只往一邊放 這樣就不會多統計了 #include<bits/stdc++.h> #d

c語言分解質因數

/******************************  *輸入一個整數,並分解質因數  *流程:(1)如果為1和2直接輸出  *(2):偶數進入do迴圈,防止因為i的自增而出錯;  *(3)

藍橋杯分解質因數

問題描述  求出區間[a,b]中所有整數的質因數分解。輸入格式  輸入兩個整數a,b。輸出格式  每行輸出一個數的分解,形如k=a1*a2*a3...(a1<=a2<=a3...,k也是從小

NOIP模擬賽密碼鎖

提示 都是 sin urn 輸入 題目 fin i++ 元素 題目描述 hzwer有一把密碼鎖,由N個開關組成。一開始的時候,所有開關都是關上的。當且僅當開關x1,x2,x3,…xk為開,其他開關為關時,密碼鎖才會打開。 他可以進行M種的操作,每種操作

HDU 1013.Digital Roots模擬或數論8月16

process clu auto white art cst int posit val Digital Roots Problem Description The digital root of a positive integer is found by su

Qt入門實踐Qt之哲學家問題(linux 多線程)

avi .cpp private 1=1 endif debug 問題 tel pub 轉載請註明出處:http://blog.csdn.net/feng1790291543 linux多線程實現哲學家問題,依據哲學家吃飯、拿筷子、放下筷子...... 首先,主

2017.07.10NOIP提高組模擬賽B組

font mic 集合點 之間 現在 problem 決定 family 打破 Summary   今天題目總體不是難,但是分數很低,只有100+10+30,其中第二題還是以前做過的,第一題設計數論,而且以前做過同一個類型的題目,比賽推了很長時間。第三題時以前做過的原題,

2017.07.11NOIP提高組模擬賽B組

span 結果 數組 運算 重要 eight 一點 對數 理解 Summary   今天的比賽打得還不錯,第一題被同桌灌輸的貪心,純模擬洗腦了,然後steal的看了一下,發現怎麽也對不了,一直在檢查。最後10分鐘才找出反例,推出動態規劃方程,沒有想到怎麽轉移,比賽就結束了

2017.07.14NOIP提高組模擬賽B組

noi net 處理 contest 比賽 size 很大的 pan 每天 Summary   這次比賽因為遲到了,少了很多時間,也受到了相應的懲罰,這是好的,是個標記牌,警醒著我。這次比賽的題目很難,也就是說,大家的得分都很低,總的來說,收獲還是很大的,因為有非常多的技

NOIP模擬賽藏寶圖 最小生成樹

names pri double span -- string noip getchar n! 性質:我們把最小生成樹建出來,如果其距離符合那麽就是對的,不符合就是錯的 因為這是個n^2的圖所以不能Kruskal只能Prim #include <cstdio>

2017.08.05NOIP提高組模擬賽B組

第一題 mar pan soft 提高 題目 mil font cal Summary   這次比賽打得非常差,第一題我以為是個難題,於是推了一下就沒再去想了,然而考場上一堆人AC。第二題狀態設錯了,導致結果有後效性。結束後pascal卡常卡了36次。第三題別人n&sup

NOIP 模擬賽鐘 模擬+鏈表

color blog 不常用 get 循環鏈表 fig problem bsp 常用 biubiu~~ 這道題實際上就是優化模擬,就是找到最先死的讓他死掉,運用時間上的加速,題解上說,要用堆優化,也就是這個意思。 對於鏈表,單項鏈表和循環鏈表都不常用,最常用的是雙向鏈表

NOIP 模擬賽中值濾波 打表找規律

超出 數學 printf clas col else 中值濾波 include noi 對於這樣看起來不像什麽算法也沒什麽知識點的題,一臉懵逼的話不是手推規律就是打表找規律......... 當然還有一些超出你能力之外的數學題...... #include <

NOIP模擬賽

== pen pan 復雜度 max mes algo 輸出 sin 【問題描述】 萌蛋有??顆珠子,每一顆珠子都寫有一個數字。萌蛋把它們用線串成了環。我們稱一個數字串是有趣的,當且僅當它的第 1 位是 2,且除了第 1 位以外的每一位都是 3。例如,2,233,233

NOIp模擬賽Walk

using 出發 col input note out 無法 lar lin Input file: walk.inOutput file: walk.outTime limit: 1 secondsMemory limit: 128 megabytes在比特鎮一共有 n

NOIp模擬賽Tourist Attractions

|| color turn stdout sin int div 表示 getchar() Input file: tour.inOutput file: tour.outTime limit: 1 secondsMemory limit: 128 megabytes在美麗

NOIp模擬賽String Master

sin for style clu amp ans idt int span Input file: master.inOutput file: master.outTime limit: 1 secondsMemory limit: 128 megabytes 所謂最長公

NOIp模擬賽value

har urn pac freopen getch amp class 貪心 max 感覺動規這種東西怎麽也學不會了..... 分析 貪心+動規 http://blog.csdn.net/chen1352/article/details/52234046 說的已經很好了。

NOIp模擬賽antipalindrome

sin 不用 const -1 ges getc print log ans 分析 知識點:排列組合問題。 本題貌似和回文字符串沒有太大的關系。 仔細劃一下應該就能知道最後的答案是:$ans=m*(m-1)*(m-2)*....*(m-2)$ 但是還是有很多坑的,數據很