codeforces 1424J,爲了過這題,我把祖傳的C++都用上了!

你們好,咱們選擇的是Bubble Cup比賽Div2場次的J題,不用問我Bubble Cup是什麼比賽,我也不清楚。總之是一場算法比賽就是了。多是這個比賽知名度比較低吧,參與的人數也不是不少,咱們選擇了一道中等經過人數的J題,做爲今天的題目。node

連接:https://codeforces.com/contest/1424/problem/Jios

這題很是不錯,是一道質量很高的數學題,也很符合個人胃口。由於沒有太多的trick,有的只有思惟和邏輯的碰撞。web

題意

咱們都知道對於兩個數a和b來講,咱們能夠很容易求到它們的最大公約數 。咱們假設a和b的最大公約數是k,若是 這三個數的長度能夠構成一個合法的三角形,那麼咱們就認爲a和b是互相友好的。算法

對於一個合法三角形而言,咱們假設它的三條邊長度分別是a,b,c,必需要有a + b > c, a + c > b, b + c > a,也就是任意兩邊之和大於第三邊。這個應該是小學數學教的內容,你們應該都很瞭解。數組

若是在一個集合當中,某一個數找不到友好的數,那麼就認爲它是孤獨的。如今咱們給定一系列的n,表示1-n的天然數構成的集合,咱們要求的是在這個集合當中孤獨的數的數量。編輯器

樣例

首先給定一個t( ),表示測試數據的數量。svg

接着給定一行t個整數,表示不一樣的n( )。對於每個n,輸出一個整數,表示1-n的天然數組成的集合當中孤獨的數的數量。測試

n=5時,孤獨數分別是一、三、5。n=10時,孤獨的數分別是一、五、7.flex

題解

因爲n的範圍是1e6,因此是不可能接受 的算法的,所以咱們不可能枚舉全部兩個數構成的組合狀況。因此暴力求解是行不通的,咱們必須分析題目,得出其餘的結論從而來簡化問題。ui

首先咱們很容易發現,1必定是孤獨的。緣由也簡單,對於任意天然數x,它與1的最大公約數都是1。那麼帶來的結果就是一、1和x。因爲x不能和1重複,因此x最小是2,但即便是2,也不知足1+1>2,因此必定沒法構成三角形。因此不論n是多少,1必定都是孤獨的。

另外比較容易想到的點就是互質的狀況,假設a和b互質,也就是說它們的最大公約數是1。這樣咱們獲得的三角形的三邊就是一、a、b。一樣a和b不相等,因此a和b至少相差1,因此也沒法構成三角形的三邊。因此若是兩個數互相互質,那麼必定不是友好的。從這點回過頭來看,其實1之因此是孤獨的,正是由於它與其餘全部數都互質。

從這點出發咱們又能夠想到什麼呢?

對了,能夠想到質數。質數與其餘全部天然數的最大公約數要麼是1,要麼是它自己。對於最大公約數是1的狀況,咱們已經分析過了,下面就來分析一下最大公約數是它自己的狀況。咱們假設這個質數是x,另一個數是b,因爲x和b的最大公約數是x,說明b是x的倍數。那麼咱們將b表示成kx。

這樣咱們獲得的三角形三邊分別是x、一、k。咱們能夠獲得三個限制條件:

第三條是顯然的,咱們能夠忽略, 咱們仔細看下前面兩條。咱們把它們聯立能夠獲得: ,那麼x只有一種取值就是 。那麼k有沒有限制呢?k也是有限制的,k不能隨便取值,咱們須要保證 ,也就是 ,即

經過這麼一串分析咱們獲得了一個結論,對於一個質數x,它想要不是孤獨的,必需要知足 。反之也能夠獲得當 的時候,且x爲質數的時候,x必定是孤獨的。

如今咱們討論完了質數的狀況,可是對於合數的狀況咱們還不知道,那麼會不會存在一些合數也是孤獨的呢?實際上是不會的,咱們能夠這樣來證實。咱們假設咱們討論的某個合數m,既然它是合數,那麼它必定能夠分解質因數。而且它必定能夠分解出一個小於 的質因數,證實過程也很簡單,由於合數要麼是平方數,要麼擁有至少兩個質因數。不管是這哪兩種狀況,只要它的質因數大於等於 ,那麼它自己必定大於n。因此這就矛盾了。

既然合數必定能夠找到一個小於 的質因數,咱們不妨假設這個質因數是x,合數m寫成ax。a的範圍應該是 。那麼咱們要作的就是尋找一個數b,使得bx能夠和ax構成友好,而且 ,且a和b互質,不然就不知足

那麼能不能找到這樣的b呢?固然是能夠的,並且很是很是簡單。咱們首先來分析一下咱們要達成的條件,因爲要使得三角形合法,咱們須要達成的條件有4個:

經過前兩個條件,咱們能夠獲得b的範圍 。可是後面兩個條件怎麼辦呢?其實很簡單,咱們能夠分狀況討論,若是 ,那麼咱們能夠選擇b = a-1,這樣的b必定知足條件。若是a < x,咱們能夠選擇 b = x。因爲x是質數,而且a < x,那麼能夠保證a和x必定互質。

這樣,咱們就證實了,全部的合數必定都不是孤獨的,它們均可以找到本身的友好數。

因此,最終咱們要求的就是大於 的質數的數量,不過不要忘了再加上1,由於1也是知足條件的孤獨數。因爲n最大有 ,若是咱們一個一個求質數確定來不及,這裏咱們可使用咱們以前介紹過的埃式篩法來快速求取全部的質數。

還有一個問題是,咱們要求某一個範圍內的質數數量應該怎麼辦?其實很簡單,咱們只須要使用前綴和便可。

除了這些以外,這題還有一個坑點就是時間卡得很緊。以致於一樣的算法Python實現的會超時,被逼無奈之下我只好用上了祖傳的C++重寫了一遍,總算是經過了。你們能夠對比一下Python和C++的效率差距,仍是挺可觀的。

最後,附上代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <string>
#include <map>
#include <set>
#include <algorithm>
#include "time.h"
#include <functional>
// 一些宏定義
#define rep(i,a,b) for (int i=a;i<b;i++)
#define Rep(i,a,b) for (int i=a;i>=b;i--)
#define foreach(e,x) for (__typeof(x.begin()) e=x.begin();e!=x.end();e++)
#define mid ((l+r)>>1)
#define lson (k<<1)
#define rson (k<<1|1)
#define MEM(a,x) memset(a,x,sizeof a)
#define L ch[r][0]
#define R ch[r][1]
using namespace std;
const int N=1000050;
const long long Mod=1000000007;
int t, query[N];
int isprime[N], primecnt[N];


// 埃式篩法
void eratosthenes() {
    rep(i, 0, N) isprime[i] = 1;
    rep(i, 2, N) {
        if (isprime[i]) {
            for (int j = i+i; j < N; j += i) {
                isprime[j] = 0;
            }
        }
    }
    // 維護質數的前綴和
    rep(i, 2, N) {
        primecnt[i] = primecnt[i-1] + isprime[i];
    }
}


int main() {
    eratosthenes();
    scanf("%d", &t);
    rep(i, 0, t) {
        int query;
        scanf("%d", &query);
        printf("%d\n", primecnt[query] - primecnt[int(sqrt(query))] + 1);
    }
    return 0;
}

今天的文章就到這裏,衷心祝願你們天天都有所收穫。若是還喜歡今天的內容的話,請來一個三連支持吧~(點贊、關注、轉發

{{uploading-image-742109.png(uploading...)}}

相關文章
相關標籤/搜索