【NOIP入門數論】分解質因數
自創題目詳解
資料包
題面
【題目名稱】分解質因數
【時間限制】1000ms
【空間限制】128M
【題目描述】
【輸入格式】
一個正整數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分演算法:
只要聽話,就一定可以拿全50分!我們只需要先將n分解質因數,並將這些質因數都記錄下來。
之後老老實實列舉a,b再用歐幾里得求出最大公約數,直接分解質因數驗證即可。
時間複雜度
下面給出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 中的質數是可以出現的,於是我們就沒有必要將它們篩去。也就是說,計算
甚至,我們可以有一個更大膽的想法:對於P_n中的質數,我們並不將其視為質數。這樣題設條件就是一種變相的互質了!!!
於是當我們求出做了手腳的尤拉函式phi[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).
具體來說,先用
凡是遇到質數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.10【NOIP提高組】模擬賽B組
font mic 集合點 之間 現在 problem 決定 family 打破 Summary 今天題目總體不是難,但是分數很低,只有100+10+30,其中第二題還是以前做過的,第一題設計數論,而且以前做過同一個類型的題目,比賽推了很長時間。第三題時以前做過的原題,
2017.07.11【NOIP提高組】模擬賽B組
span 結果 數組 運算 重要 eight 一點 對數 理解 Summary 今天的比賽打得還不錯,第一題被同桌灌輸的貪心,純模擬洗腦了,然後steal的看了一下,發現怎麽也對不了,一直在檢查。最後10分鐘才找出反例,推出動態規劃方程,沒有想到怎麽轉移,比賽就結束了
2017.07.14【NOIP提高組】模擬賽B組
noi net 處理 contest 比賽 size 很大的 pan 每天 Summary 這次比賽因為遲到了,少了很多時間,也受到了相應的懲罰,這是好的,是個標記牌,警醒著我。這次比賽的題目很難,也就是說,大家的得分都很低,總的來說,收獲還是很大的,因為有非常多的技
【NOIP模擬賽】藏寶圖 最小生成樹
names pri double span -- string noip getchar n! 性質:我們把最小生成樹建出來,如果其距離符合那麽就是對的,不符合就是錯的 因為這是個n^2的圖所以不能Kruskal只能Prim #include <cstdio>
2017.08.05【NOIP提高組】模擬賽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)$ 但是還是有很多坑的,數據很