數論複習
2017.11.28
今天看了一天的初等數論。
先上了一道比(fei)較(chang)水的題目。
HDU-2136
求一個數的最大質因數在所有質數中的排名。
數據範圍是 .
開始覺得可以在線求解然後就T掉了。
加上記憶化…… 還是T掉了。
粗略估計了一下,其實這樣的在線的計算量是很恐怖的。
以內有 個質數。
對於一個數可以拆成一個很小的質數 的次方,那麼我就要從 倒序遍歷到那個質數的 才可以返回答案,這樣的計算量單單在 個不同數的詢問都無法接受的。
複雜度大概是
呃,一共是 次。
於是決定直接離線做...
考慮到 的每一個 都可能會對 產生貢獻。
那麼我們直接對每一個 順序枚舉,設為 ,把 有可能產生貢獻的數的 全部設為 。這樣做下去,那麼數 一定被它最大的質因數最後更新。
複雜度?
相當於一般的素數篩法——埃氏篩法, 。
實際是 次
對於簡單題其實也可以細究並且仔細考慮的。
其實可以打表的……
先寫線性篩再求答案其實比較愚蠢,可以直接求答案,像埃氏篩法一樣。
# include <cstdio>bool ispr [1000005] ;int ans [1000005], primes [1000005], ptot ;void Liner_Shaker ( int N ) { for ( register int i = 2 ; i <= N ; ++ i ) ispr [i] = 1 ; for ( register int i = 2 ; i <= N ; ++ i ) { if ( ispr [i] ) { primes [ans [i] = ++ ptot] = i ; } for ( register int j = 1, to ; j <= ptot && ( to = primes [j] * i ) <= N ; ++ j ) { ispr [to] = 0 ; if ( i % primes [j] == 0 ) break ; } } for ( int i = 1 ; i <= ptot ; ++ i ) for ( int j = 2, to ; ( to = j * primes [i] ) <= N ; ++ j ) { ans [to] = i ; }}inline int MaxPrimeFactor ( int x ) { return ans [x] ;}int main ( ) { Liner_Shaker ( 1000000 ) ; int n ; while ( ~ scanf ( "%d", & n ) ) { printf ( "%d
", MaxPrimeFactor ( n ) ) ; }}
經過Doggu神犇的指點,這道題可以 的時間解決。但實際效果和 差不多。
我們可以在線性篩直接預處理出每個數 的最小質因數 和最大質因數 。
首先對於一個質數 ,
對於其他的任意一個數:
我已經ZZ到把這個忘了,對於這道題,直接存下標就可以了。
# include <cstdio>bool ispr [1000005] ;int maxp [1000005], primes [1000005], ptot ;void Liner_Shaker ( int N ) { for ( register int i = 2 ; i <= N ; ++ i ) ispr [i] = 1 ; for ( register int i = 2 ; i <= N ; ++ i ) { if ( ispr [i] ) { primes [maxp [i] = ++ ptot] = i ; } for ( register int j = 1, to ; j <= ptot && ( to = primes [j] * i ) <= N ; ++ j ) { ispr [to] = 0 ; maxp [to] = maxp [i] ; if ( i % primes [j] == 0 ) break ; } }}inline int MaxPrimeFactor ( int x ) { return maxp [x] ;}int main ( ) { Liner_Shaker ( 1000000 ) ; int n ; while ( ~ scanf ( "%d", & n ) ) { printf ( "%d
", MaxPrimeFactor ( n ) ) ; }}
然後還是一道比較水的題
BZOJ2296
給一個數 ,構造出一個數 ,使得 包含 到 所有的數字,並且 。無解輸出 。 , 。
考慮到需要包含 到 所有的數字,我們可以先構造一個數字 使得 已經滿足要求,然後再在 的基礎上添加數字,使得滿足 。
如何添加,假設當前的 ,那麼我的 一定滿足要求。且 ,那麼只要我的 最後的 位都是0,就對第一個條件沒有影響了。
可是這樣的話最後的結果會大於 吖。
所以 取為 (或者是 這樣的數字)是最好的,最極端的 取到 答案會是 ,不會錯誤,而對於 , 取到 答案會是 就會失掉一個數字。
/************************************************************** Problem: 2296 User: Lazer2001 Language: C++ Result: Accepted Time:0 ms Memory:820 kb****************************************************************/ # include <cstdio># include <cassert> int main ( ) { const long long base = 1236857490 * 1000000LL ; int T ; scanf ( "%d", & T ) ; while ( T -- ) { long long x ; scanf ( "%lld", & x ) ; printf ( "%lld
", x == 0 ? -1LL : base + ( x - base % x ) ) ; }}
2017.11.29
然後還是一個很ZZ的題,本來20mins切掉的搞了近一個小時。
因為我的CRT裡面求出來有負數……
BZOJ-1951
一句話題意:
求
其中
大水題……
歐拉定理
所以 的循環節為 ,
則 (自己證明或者百度,比較簡單)
還有它的推廣, (這個證明比較繁瑣見這裡)
由歐拉定理
轉化為當 時
時,答案為 .
當然可以一起處理用歐拉定理的推廣
直接合併處理。
對於題目中的 是一個質數
故
這個數不是質數,分解質因數 。
我們發現每一個質因數的次冪都是 ,所以直接分成每一個質因數用 定理處理組合數然後得到四個同餘式用中國剩餘定理合併起來,最後得到指數用快速冪計算。
否則就要用拓展 定理。
定理和中國剩餘定理什麼的就不展開說了。
/************************************************************** Problem: 1951 User: Lazer2001 Language: C++ Result: Accepted Time:184 ms Memory:2792 kb****************************************************************/ # include <bits/stdc++.h> class CRT { private : int a [5], m [5] ; inline void exgcd ( int a, int b, int& x, int& y ) { if ( ! b ) { x = 1, y = 0 ; return ; } exgcd ( b, a % b, y, x ) ; y -= a / b * x ; } public : CRT ( ) { } CRT ( int* a, int* m ) { for ( int i = 0 ; i < 4 ; ++ i ) this -> a [i] = a [i], this -> m [i] = m [i] ; } inline long long main ( ) { int N ( 1 ), rt ( 0 ) ; for ( int i = 0 ; i < 4 ; ++ i ) N *= m [i] ; for ( int i = 0 ; i < 4 ; ++ i ) { int t = N / m [i] ; int x, y ; exgcd ( t, m [i], x, y ) ; rt += 1LL * t * x % N * 1LL * a [i] % N ; while ( rt < 0 ) rt += N ; while ( rt >= N ) rt -= N ; } return rt ; }} Crt ; # define N 100010# define RG register class Solution { private : int Mod ; int inv [N], fac [N] ; inline int Lucas ( int n, int m ) { if ( m > n ) return 0 ; if ( n < Mod ) return 1LL * fac [n] * inv [m] % Mod * 1LL * inv [n - m] % Mod ; return 1LL * Lucas ( n / Mod, m / Mod ) * Lucas ( n % Mod, m % Mod ) % Mod ; } public : Solution ( ) { } Solution ( const int& Mod ) : Mod ( Mod ) { inv [0] = inv [1] = 1, fac [0] = fac [1] = 1 ; // fac [0]!!! for ( RG int i = 2 ; i < Mod ; ++ i ) fac [i] = 1LL * i * fac [i - 1] % Mod ; for ( RG int i = 2 ; i < Mod ; ++ i ) inv [i] = 1LL * ( Mod - Mod / i ) * inv [Mod % i] % Mod ; for ( RG int i = 2 ; i < Mod ; ++ i ) inv [i] = 1LL * inv [i] * inv [i - 1] % Mod ; } inline int main ( int n ) { int ans ( 0 ) ; for ( RG int i = 1, l = sqrt ( n + 0.5 ), tmp ; i <= l ; ++ i ) if ( n % i == 0 ) { tmp = n / i ; if ( tmp != i ) ans += Lucas ( n, tmp ) ; ans += Lucas ( n, i ) ; while ( ans >= Mod ) ans -= Mod ; } return ans ; }} Sol ; inline int mpow ( register int a, register int x, const int& Mod, register int rt = 1 ) { while ( x ) { if ( x & 1 ) rt = 1LL * rt * a % Mod ; a = 1LL * a * a % Mod ; x >>= 1 ; } return rt ;} # undef N# undef RG int mod [] = { 2,3,4679,35617 } ;int ans [4] ; int main ( ) {// freopen ( "in.txt", "r", stdin ) ; int g, n ; scanf ( "%d%d", & n, & g ) ; if ( ( g %= 999911659 ) == 0 ) return puts ( "0" ), 0 ; for ( int i = 0 ; i < 4 ; ++ i ) { Sol = Solution ( mod [i] ) ; ans [i] = Sol.main ( n ) ; } Crt = CRT ( ans, mod ) ; printf ( "%d
", mpow ( g, Crt.main ( ), 999911659 ) ) ;}
乘法原理水題
BZOJ-2751
題意:
已知 個位置,每個位置可以放 中的數,有 個限制條件 表示 位置上不能放數 。
定義一個數列的積為數列中各個元素的乘積,求所有可能數列的積之和。
結果對 取模。
其中 .
我們先考慮沒有限制條件,即 的情況。
每個位置都可以取遍 中的數,我們來推導這種情況的結果(雖然顯然)。
用 來表示 位置的取值
結果為
結果為
結果為
結果為
對於每一個位置變化的答案為
那麼答案為
回到原題,
所以對於無限制的位置,設有 個,答案是
設 個位置有限制,對於有限制的位置 ,暴力計算每個位置的貢獻 。
然後根據乘法原理乘起來。
雖然可以一次 維護不同.但是我還是想寫 .
/************************************************************** Problem: 2751 User: Lazer2001 Language: C++ Result: Accepted Time:376 ms Memory:6972 kb****************************************************************/ # include <bits/stdc++.h> inline int read ( ) { register int x, c ; while ( isspace ( c = getchar ( ) ) ) ; for ( x = -48 + c ; isdigit ( c = getchar ( ) ) ; ( x *= 10 ) += c - 48 ) ; return x ;} # define N 100010 std :: map < int, std :: set < int > > S ; # undef N # define Mod 1000000007 inline int mpow ( register int a, register int x, register int rt = 1 ) { for ( ; x ; x >>= 1, a = 1LL * a * a % Mod ) if ( x & 1 ) { rt = 1LL * rt * a % Mod ; } return rt ;} int main ( ) { int n ( read ( ) ), m ( read ( ) ), k ( read ( ) ) ; while ( k -- ) { int pos ( read ( ) ), v ( read ( ) ) ; S [pos].insert ( v ) ; } int remain_pos = m ; int rt = 1 ; const int sum = 1LL * n * ( n + 1 ) % Mod * ( ( Mod + 1 ) >> 1 ) % Mod ; // ( Mod + 1 ) >> 1 = inv (2) for ( std :: map < int, std :: set < int > > :: iterator it0 = S.begin ( ) ; it0 != S.end ( ) ; ++ it0 ) { -- remain_pos ; int tmp = sum ; for ( std :: set < int > :: iterator it1 = ( it0 -> second ).begin ( ) ; it1 != ( it0 -> second ).end ( ) ; ++ it1 ) tmp -= *it1 ; rt = 1LL * rt * tmp % Mod ; while ( rt < 0 ) rt += Mod ; // alternative !!! } printf ( "%d
", 1LL * rt * mpow ( sum, remain_pos ) % Mod ) ; return 0 ;} # undef Mod
推薦閱讀: