題解
A - Prime Gap
題目:讀入一個n,是素數就輸出0,不是就輸出它左右兩邊最近的兩個素數形成的區間的長度。
- 素數線性篩
- 思路:埃式篩法打表(素數表),用結合比較快的素數判定函數。
- 注意點:上限是1299709,可以設成
const int maxn = 1299709+100;
- 判定素數函數部分
bool isprime(int n)
{
if(n < 2)return 0;
if(n ==2 || n == 3)return 1;
if(n % 6 != 1 && n % 6 != 5)return 0;
for(int i = 5; i * i <= n; i += 6)
{
if(n % i == 0 || n % (i + 2) == 0)
return 0;
}
return 1;
}
- 打表部分(埃式)
//構造素數表
memset(bl, 0, sizeof bl);
for(int i=2; i<maxn; ++i)
for(int k=i*2; k<maxn; k+=i)
bl[k]=1;
int num=0;
for(int i=2; i<maxn; ++i)//素數隊列
if(bl[i]==0)
prime[num++]=i;
- 邏輯部分
int n;
while(cin>>n)
{
if(n==0)
break;
if(isprime(n))
{
cout<<0<<endl;
continue;
}
for(int i=0; i<num; ++i)
{
if(prime[i]<n&&prime[i+1]>n)
cout<<(prime[i+1]-prime[i])<<endl;
}
}
B - Relatives
題目:求單個歐拉函數
- 求單個歐拉函數
- 樣題
#include <iostream>
using namespace std;
int eular(int n) //求單個數的歐拉函數
{
int ans = n;
for(int i = 2; i*i <= n; i++)
{
if(n % i == 0)
{
ans = ans/i*(i-1); //ans=ans*(1-1/i)=ans*(i-1)/i
// =ans/i*(i-1) //先除防止溢出
while(n % i == 0) //消除i因子
n /= i;
}
}
if(n > 1)ans = ans/n*(n-1); //最後可能還剩下一個質因數沒有除
return ans;
}
int main()
{
int n;
while(cin >> n && n)
{
cout << eular(n) << endl;
}
return 0;
}
C - Sum of Consecutive Prime Numbers
題目:讀入一個正整數n,求有多少種方式可以把它表示成幾個連續的素數的和。
- 素數線性篩(一個正整數轉換多個連續的素數的和)
- 打表(埃式篩法)+暴力枚舉
- 注意點:幾個
連續的
素數的和 - 還用A題的打表模板,下面是邏輯部分
int n;
while(cin>>n&&n)
{
int cnt(0);
for(int i=0; prime[i]<=n; i++)
{
int sum(0);
for(int j=i; ; j++)
{
if(sum == n)
{ cnt++; break; }
else if(sum > n) break;
else sum += prime[j];
}
}
cout << cnt << endl;
}
D - Farey Sequence
題目:讀入正整數n,求1到n裏面有幾對互質數。(如果 ,則稱a,b爲互質數)
- 歐拉函數
- 思路:
- 分析:找到所有的互質數對,從2開始每一個數找它前面的存在的互質數,恰好符合歐拉函數的特點。
- 做法:構造歐拉表(打表)
- 構造歐拉表(埃式;模板)。直接函數讀入
maxn
的值就可以打表了。
void euler(int n)
{
for (int i=1; i<=n; i++) phi[i]=i;
for (int i=2; i<=n; i++)
{
if (phi[i]==i)//這代表i是質數
{
for (int j=i; j<=n; j+=i)
{
phi[j]=phi[j]/i*(i-1); //把i的倍數更新掉
}
}
}
}
- 另一種方法
memset(a,0,sizeof a);
//構造歐拉表
for(int i=2; i<=mx; i++)
{
if(a[i]==0)
{
for(int j=i; j<=mx; j+=i)
{
if(a[j]==0)
a[j]=j;
a[j]=a[j]/i*(i-1);
}
}
}
E - Visible Lattice Points
題目;方格網中取個座標點,左下角爲(0,0),求從(0,0)點可以看到幾個點。(從原點看去,在同一條線上的點只能看見最前面的點)
- 多個數的歐拉函數
- 思路:
- 分析:可以知道,可見的點的總數,等於按照對角線分開的其中一個三角形的可見點的二倍加上斜率爲1的那條線上的一個點。對一個三角形分析可知,n增加的時候,點數增加的數對應着新的斜率。如圖。
(圖片上有點錯誤,n=4時,不是兩個1/3,而是一個1/3一個2/3) - 做法:歐拉表加輸出調整;按照歐拉函數求那些斜率大於0小於1的線對應的點數的二倍,最後加3(一個斜率爲0,一個斜率爲1,一個沒有斜率)
- 對應代碼
//n*n的方格,從(0,0)點可以看到幾個點
#include <iostream>
using namespace std;
const int mx = 1000;//開大了也可能出現run的問題問題
int phi[mx+10];
void euler(int n)
{
for (int i=1; i<=n; i++) phi[i]=i;
for (int i=2; i<=n; i++)
{
if (phi[i]==i)//這代表i是質數
{
for (int j=i; j<=n; j+=i)
{
phi[j]=phi[j]/i*(i-1); //把i的倍數更新掉
}
}
}
}
int main()
{
int n, t;
euler(mx);
int cnt=1;
cin >> t;
for(; cnt<=t; cnt++)
{
cin >> n;
int sum(0);
for(int i=2; i<=n; i++)
sum+=phi[i];
//就是改了個輸出,分析可知n的增加,對應着增加了1/n,2/n,3/n````(n-1)/n,而且
//其中的假分數代表被擋到,看不見了,所以還是歐拉數列,每個n是前面那個數對應的歐拉值的和
//再加上斜率爲0和斜率爲1的三個點
cout << cnt << " " << n << " " <<sum*2+1+2<< endl;
}
return 0;
}
F - Almost Prime Numbers
題目:定義
almost prime
就是除了1和本身僅可以被一個素數整除的非素數,求一個給定區間裏的almost prime
數量。
如4,8就是符合條件的數
- 素數線性篩
- 思路:打表+暴力枚舉
- 對應代碼
- 注意點:這裏的
long long
很重要
//自己的按照a題弄的,錯在了沒用ll,一直沒發現
#include<iostream>
#include<string.h>
using namespace std;
#define ll long long
const int maxn = 1000005;
ll prime[maxn],vis[maxn];
int main()
{
//構造素數表
memset(vis, 0, sizeof vis);
for(ll i=2; i<=maxn; i++)
for(ll k=i*2; k<=maxn; k+=i)
vis[k]=1;
ll len=0;
for(ll i=2; i<=maxn;i++)//素數隊列
if(vis[i]==0)
prime[len++]=i;
ll t;
cin>>t;
while (t--)
{
ll cnt = 0;
ll l,r;
cin>>l>>r;
for (ll i = 0; i < len; i++)
for ( ll j = prime[i]*prime[i]; j <= r; j*=prime[i] )
if ( j >= l )
cnt++;
cout<<cnt<<endl;
}
return 0;
}