ACM新手DAY 20 歐拉函數&線性篩

題解

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裏面有幾對互質數。(如果 gcd(a,b)=1gcd(a,b)=1,則稱a,b爲互質數)

  • 歐拉函數
  • 思路:
  1. 分析:找到所有的互質數對,從2開始每一個數找它前面的存在的互質數,恰好符合歐拉函數的特點。
  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

題目;方格網中取(n+1)(n+1)(n+1)*(n+1)個座標點,左下角爲(0,0),求從(0,0)點可以看到幾個點。(從原點看去,在同一條線上的點只能看見最前面的點)

  • 多個數的歐拉函數
  • 思路:
  1. 分析:可以知道,可見的點的總數,等於按照對角線分開的其中一個三角形的可見點的二倍加上斜率爲1的那條線上的一個點。對一個三角形分析可知,n增加的時候,點數增加的數對應着新的斜率。如圖。題解配圖
    (圖片上有點錯誤,n=4時,不是兩個1/3,而是一個1/3一個2/3)
  2. 做法:歐拉表加輸出調整;按照歐拉函數求那些斜率大於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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章