【題解】線性篩素數 線性統計範圍質因數 小Y的智力遊戲

對於思維有一定鍛鍊


關於“線性統計範圍質因數”是指統計1到n範圍每個數的質因數的總冪次(也可以理解爲統計n!的每個質因數的冪次)
例:n = 10時
分解質因數分別有

(1),2,3,22,5,23,7,23,32,25

那麼結果應爲
8,3,2,1

表示2的總冪次是8,3的總冪次是3,5的總冪次爲2,7的總冪次爲1

題目

題目描述

小 Y 最近迷上了一款智力遊戲。(當然小 Y 還是很好學的)
這款智力遊戲就是讓你從 1 到 N 這 N 個數中,選取若干個不相等的正整數,使得它們的乘積爲一個
完全平方數,同時,這個乘積也將成爲你的得分。
他(或她,以後這個括號就省略了)想知道最大得分對 1000000007 的模值。

輸入

輸入文件名 game.in
一行一個正整數 N,代表數字的個數。

輸出

輸出文件名 game.out
一行一個正整數,代表最大得分對 1000000007 的模值。

輸入輸出樣例

輸入輸出樣例】

Game.in Game.out
3 1

數據範圍

對於 30%的數據,保證有 n≤50。
對於 60%的數據,保證有 n≤1000。
對於 70%的數據,保證有 n≤10000。
對於 80%的數據,保證有 n≤100000。
對於 90%的數據,保證有 n≤1000000。
對於 100%的數據,保證有 n≤3000000。


題解

可以想到,將結果質因數分解後每個質因數的冪次一定是偶數才能是完全平方數
那麼就轉換爲求出答案的每個質因數冪次最多可以是多少
爲保證一定是由1到n中不相等的數乘來,直接從n!的質因數入手即可
根據數據範圍現在就是要尋求快速統計n!質因數冪次的方法
有兩種方法,按時空複雜度優秀度升序分別闡述

方法一

首先要統計質因數冪次肯定怎樣都要先把質數求出來,只談複雜度Onlogn和On的求法都可以,但是可以利用On的線性求法使統計冪次更快求出
在線性篩中,是通過保證每個數都只被最小的質因數計算一遍來實現線性複雜度,現在我們要統計冪次,也可以在線性篩時一起保存每個數的最小質因數,這樣在統計時就可以將n不斷縮小。具體來說:每次除以當前最小質因數,並將此質因數冪次統計加1
每次至少除以2,複雜度爲nlogn
(將求解問題轉化爲另一個同樣的範圍更小的問題)

方法二


180204 update
這是“勒讓德定理


雖然方法一對於本問題已經完全足夠,但是還能更優!
由txc同學本次考試獨立發現(ysp也是同樣的方法)
感謝txc的解釋
方法一需要開大小爲n的int數組在線性篩時來存,方法二可根據n直接求出
txc是以質因數含2爲例將2, 4, 6, 8…列出,求出每個數含2的冪次,觀察可發現規律
不過我觀察代碼,覺得還可以有這種解釋
對於每個質因數,可每一次都對每個數都試除,每次加上有此質因數的個數,直至不能再除即可
當然具體操作不是直接這麼操作,其實每次拿n除以當前質因數向下取整所得就是由此質因數的個數。爲方便操作,不讓n變,逆向,拿n除以當前質數i的次冪,直至當前質數i次冪大於n爲止
以n = 18,當前質數p = 2爲例
2, 4, 6, 8, 10…含二冪次爲
1, 2, 1, 3, 1, 2, 1, 4, 1
i = 1時
0, 1, 0, 2, 0, 1, 0, 3, 0
i = 2時
0, 0, 0, 1, 0, 0, 0, 2, 0

每次變化的個數就是

n除以當前質因數向下取整所得

大概算時間複雜度也是nlogn,但測試速度小於方法一
空間複雜度也更小


代碼

#include <cstdio>

typedef long long ll;
const int maxr = 216820, maxn = 3e6 + 100, mod = 1000000007;
bool np[maxn];
int pri[maxr], tot = 0, cnt[maxn];
ll QPow(ll x, int n){
    n -= n & 1;
    ll res = 1;
    while (n){
    if (n & 1) res = res * x % mod;
    x = x * x % mod; n >>= 1;
    }return res;
}
int main (){
    int n; scanf ("%d", &n);
    for (int i = 2; i <= n; ++i){
    if (!np[i])pri[++tot] = i;
    for (int j = 1, t = pri[1] * i; j <= tot && t <= n; t = pri[++j] * i){
        np[t] = 1; 
        if (i % pri[j] == 0) break;
    }
    }
    ll sum;
    for (int i = 1; i <= tot; ++i){
    sum = 1;
    while (sum <= n){
        sum *= pri[i];
        cnt[i] += n / sum;
    }
    }
    ll ans = 1;
    for (int i = 1; i <= tot; ++i) ans = ans * QPow(pri[i], cnt[i]) % mod;
    printf ("%lld", ans);

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章