【全程NOIP计划】简单数论
基础知识与记号
∀ \forall ∀ ,∃ \exist ∃
整除 :如果$a=bk , 其中 ,其中 , 其 中 a,b,k都是整数,则 都是整数,则 都 是 整 数 , 则 b整除 整除 整 除 a,记作 ,记作 , 记 作 b|a$
也称作b是a的约数(因数),a是b的倍数
如何求出n的所有约数?
如果a是n的约数,则n/a也是n的约数,且a和n/a中必有一个小于等于根号n
所以我们直接枚举1到根号n之间的所有数,判断是不是n的因数就可以了
a和b的最大公因数记为gcd ( a , b ) \gcd(a,b) g cd( a , b ) ,或者( a , b ) (a,b) ( a , b )
特殊地
1.( a , a ) = ( 0 , a ) = a (a,a)=(0,a)=a ( a , a ) = ( 0 , a ) = a
2.如果a ∣ b a|b a ∣ b ,则( a , b ) = a (a,b)=a ( a , b ) = a
3.( a , b ) = ( a , a + b ) = ( a , k a + b ) , ( a , b ) = ( b , a m o d b ) (a,b)=(a,a+b)=(a,ka+b),(a,b)=(b,a \space mod \space b) ( a , b ) = ( a , a + b ) = ( a , k a + b ) , ( a , b ) = ( b , a m o d b )
4.( k a , k b ) = k ∗ ( a , b ) (ka,kb)=k*(a,b) ( k a , k b ) = k ∗ ( a , b )
5.( a , b , c ) = ( ( a , b ) , c ) (a,b,c)=((a,b),c) ( a , b , c ) = ( ( a , b ) , c )
6.如果( a , b ) = 1 (a,b)=1 ( a , b ) = 1 则称a和b互质,记为a ⊥ b a \bot b a ⊥ b
对于整数a,b,b > 0 b>0 b > 0 ,则存在唯一的整数q,r,满足a = b q + r a=bq+r a = b q + r ,其中0 ≤ r < b 0 \leq r <b 0 ≤ r < b
a ÷ b = q … … r a \div b = q \dots \dots r a ÷ b = q … … r
其中称q为商,r为余数,余数r用a m o d b a \space mod \space b a m o d b 表示
如果有两个整数a满足除以b的余数为r,则我们称a ≡ a ′ ( m o d b ) a \equiv a'(mod \space b) a ≡ a ′ ( m o d b )
唯一分解定理
对于一个整数n,它可以唯一分解成n = ∏ p i k i n= \prod p_i^{k_i} n = ∏ p i k i
例如36可以唯一分解为36 = 2 2 × 3 2 36=2^2 \times 3^2 3 6 = 2 2 × 3 2
定义域为整数的函数被称为数论函数
对于数论函数f f f
1.如果∀ a ⊥ b , f ( a b ) = f ( a ) f ( b ) \forall a\bot b ,f(ab)=f(a)f(b) ∀ a ⊥ b , f ( a b ) = f ( a ) f ( b ) ,则称f f f 为积性函数
2.如果∀ a , b , f ( a b ) = f ( a ) f ( b ) \forall a,b,f(ab)=f(a)f(b) ∀ a , b , f ( a b ) = f ( a ) f ( b ) ,则称f f f 为完全积性函数
例如f ( x ) = x f(x)=x f ( x ) = x 就是一个完全积性函数
欧几里得算法
给定a , b a,b a , b ,快速算出( a , b ) (a,b) ( a , b )
引理:若a < b a<b a < b ,则b m o d a < b / 2 b \space mod \space a<b/2 b m o d a < b / 2
不断运用( a , b ) = ( a , b m o d a ) (a,b)=(a,b\space mod \space a) ( a , b ) = ( a , b m o d a ) ,直到某个数为0
至少运行log 2 a \log_2^a log 2 a 次或者l o g 2 b log_2^b l o g 2 b 次,就会有某个数变成0
1 2 3 4 int gcd (int a,int b) { return b? gcd (b,a%b):a; }
拓展欧几里得算法
裴蜀定理
∀ a , b , ∃ x , y , s . t . a x + b y = ( a , b ) \forall a,b, \exist x,y,s.t. ax+by=(a,b) ∀ a , b , ∃ x , y , s . t . a x + b y = ( a , b )
使用构造法来证明这个东西
当b=0时,x=1,y=0
否则:
$
ax+by
\=(\lfloor \frac a b \rfloor*b+a \mod b)x+by
\=b(y+\lfloor \frac a b \rfloor x)+(a\mod b)
\=gcd(a,b)
如果已经求出来
如果已经求出来 如 果 已 经 求 出 来 bx’+(a \mod b)y’=gcd(a,b)的解,则 的解,则 的 解 , 则 x=y’,y=x’-\lfloor \frac a b \rfloor y’$
1 2 3 4 5 6 7 8 9 10 void exgcd (int a,int b,int &x,int &y) { if (b==0 ) { x=1 ,y=0 ; return ; } exgcd (b,a%b,y,x); y-=a/b*x; }
如何求出a x + b y = ( a , b ) ax+by=(a,b) a x + b y = ( a , b ) 所有的解?
先用exgcd求出一组x0,y0
其他的解为:x = x 0 + k b ( a , b ) , y = y 0 − k a ( a , b ) x=x_0+k \frac b {(a,b)},y=y_0-k \frac a {(a,b)} x = x 0 + k ( a , b ) b , y = y 0 − k ( a , b ) a
其中最小的正数x为( b ( a , b ) + x 0 m o d b ( a , b ) ) m o d b ( a , b ) (\frac b {(a,b)}+x_0 \mod \frac b {(a,b)}) \mod \frac b {(a,b)} ( ( a , b ) b + x 0 m o d ( a , b ) b ) m o d ( a , b ) b
欧拉函数
ϕ ( n ) \phi(n) ϕ ( n ) 表示1~n中与n互质的数的个数,称为欧拉函数
例如1~12中,1,5,7,11和12互质,则ϕ ( 12 ) = 4 \phi(12)=4 ϕ ( 1 2 ) = 4
怎么求?
设n = ∏ p i k i n =\prod p_i^{k_i} n = ∏ p i k i 则ϕ ( n ) = n ∏ ( 1 − 1 p i ) = ∏ p i k i − 1 ∗ ( p i − 1 ) \phi(n)=n \prod(1-\frac 1 {p_i})=\prod p_i^{k_i-1}*(p_i-1) ϕ ( n ) = n ∏ ( 1 − p i 1 ) = ∏ p i k i − 1 ∗ ( p i − 1 )
枚举质因子计算就可以了,时间复杂度O ( n ) O( \sqrt{n}) O ( n )
(拓展)欧拉定理(欧拉函数)
欧拉定理:∀ a ⊥ b , a ϕ ( n ) ≡ 1 ( m o d n ) \forall a \bot b ,a^{ \phi (n)} \equiv 1 (\mod n) ∀ a ⊥ b , a ϕ ( n ) ≡ 1 ( m o d n )
拓展欧拉定理:∀ a , n , a 2 ϕ ( n ) ≡ a ϕ ( n ) ( m o d n ) \forall a,n,a^{2 \phi(n)} \equiv a^{\phi (n)}(mod \space n) ∀ a , n , a 2 ϕ ( n ) ≡ a ϕ ( n ) ( m o d n )
也就是说,b > ϕ ( n ) → a b ≡ a b m o d ϕ ( n ) + ϕ ( n ) ( m o d n ) b> \phi(n) \to a^b \equiv a^{b \space mod \space \phi(n)+\phi(n)}(mod \space n) b > ϕ ( n ) → a b ≡ a b m o d ϕ ( n ) + ϕ ( n ) ( m o d n )
P5110 块速递推
思路
先运用拓展欧拉定理 ,将b缩小到2 ϕ ( c ) < 2 c 2\phi(c)<2c 2 ϕ ( c ) < 2 c 的范围之内
a b = ( a c ) ⌊ b c ⌋ ∗ a b m o d c a^b=(a^{ \sqrt{c}})^{\lfloor \frac b {\sqrt{c}}\rfloor}*a^{b \space mod \space c} a b = ( a c ) ⌊ c b ⌋ ∗ a b m o d c ,其中⌊ b c ⌋ < 2 c , b m o d c < c \lfloor \frac b {\sqrt{c}} \rfloor <2 \sqrt{c},b \space mod \space \sqrt{c}< \sqrt{c} ⌊ c b ⌋ < 2 c , b m o d c < c
预处理( a c ) i , a j ( 1 ≤ i ≤ 2 c , 1 ≤ j < c ) (a^{\sqrt{c}})^i,a^j(1\le i \le 2\sqrt{c},1 \le j <\sqrt{c}) ( a c ) i , a j ( 1 ≤ i ≤ 2 c , 1 ≤ j < c ) 即可
实际上是一种分块的做法
可以预处理,然后再直接查询
实际上就是光速幂,具体模板题为LOJ.162.快速幂 2
通过题目给定的递推公式,我们首先可以推出来一个用来递推的矩阵
X = [ 233 1 666 0 ]
X=\begin{bmatrix} 233 & 1 \\
666 & 0 \end{bmatrix}
X = [ 2 3 3 6 6 6 1 0 ]
然后预处理一下
最后对于每个n,直接通过预处理的数据直接O ( 1 ) O(1) O ( 1 ) 输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> #include <cmath> using namespace std;const int maxn=50005 ;const int p=1e9 +7 ;unsigned long long T,res;namespace Mker{ unsigned long long SA, SB, SC; void init () { scanf ("%llu%llu%llu" , &SA, &SB, &SC); } unsigned long long rand () { SA ^= SA << 32 , SA ^= SA >> 13 , SA ^= SA << 1 ; unsigned long long t = SA; SA = SB, SB = SC, SC ^= t ^ SA; return SC; } } struct matrix { long long a,b,c,d; matrix () { a=b=c=d=0 ; } matrix operator *(register matrix &s) { matrix temp; temp.a=(a*s.a+b*s.c)%p; temp.b=(a*s.b+b*s.d)%p; temp.c=(c*s.a+d*s.c)%p; temp.d=(c*s.b+d*s.d)%p; return temp; } }base,m1[maxn],m2[maxn]; int main () { cin>>T; Mker::init (); base.a=233 ; base.b=666 ; base.c=1 ; m1[1 ]=base; int len=sqrt (1e9 +6 ); for (int i=2 ;i!=len;i++) m1[i]=m1[i-1 ]*base; m2[1 ]=m1[len-1 ]*base; m1[0 ].a=m1[0 ].d=1 ; m2[0 ].a=m2[0 ].d=1 ; for (int i=2 ;i<=len+1 ;i++) m2[i]=m2[i-1 ]*m2[1 ]; while (T--) { matrix ans; ans.a=1 ; int n=Mker::rand ()%1000000006 ; if (n<=1 ) res^=n; else { ans=ans*m2[(n-1 )/len]*m1[(n-1 )%len]; res^=ans.a; } } cout<<res<<'\n' ; return 0 ; }
逆元
如果a x ≡ 1 ( m o d b ) ax \equiv 1(mod \space b) a x ≡ 1 ( m o d b ) ,则称x为a关于模b的逆元,通常记作a − 1 a^{-1} a − 1
分数取模:a b ≡ a ∗ b − 1 ( m o d p ) \frac ab \equiv a*b^{-1}(mod \space p) b a ≡ a ∗ b − 1 ( m o d p ) ,
a − 1 a^{-1} a − 1 存在的充要条件是( a , b ) = 1 (a,b)=1 ( a , b ) = 1
在[ 0 , b ) [0,b) [ 0 , b ) 的范围诶,a关于模b的逆元(如果存在的话),是唯一的
等价于求a x + b y = 1 ax+by=1 a x + b y = 1 ,课可以直接使用拓展欧几里得解决
如果b是质数,则ϕ ( b ) = b − 1 , a ∗ a b − 2 = a b − 1 ≡ 1 ( m o d b ) \phi(b)=b-1,a*a^{b-2}=a^{b-1}\equiv 1(mod \space b) ϕ ( b ) = b − 1 , a ∗ a b − 2 = a b − 1 ≡ 1 ( m o d b ) ,所以逆元为a b − 2 a^{b-2} a b − 2 ,使用快速幂计算
如何在O ( n ) O(n) O ( n ) 的时间内求1~n内的模质数p的所有逆元呢?
$i^{-1} \equiv - \lfloor \frac pi \rfloor (p \space mod \space i)^{-1} \iff p \space mod \space i \equiv -\lfloor \frac pi \rfloor *i \iff \lfloor \frac pi \rfloor *i+p \space mod \space i =p \equiv (mod \space p) $
威尔逊定理
( p − 1 ) ! ≡ − 1 ( m o d p ) (p-1)! \equiv -1 (mod \space p) ( p − 1 ) ! ≡ − 1 ( m o d p )
x 2 ≡ 1 ( m o d p ) x^2 \equiv 1 (mod \space p) x 2 ≡ 1 ( m o d p ) 的解仅有x ≡ 1 , x ≡ − 1 ( m o d p ) x \equiv 1,x \equiv -1(mod \space p) x ≡ 1 , x ≡ − 1 ( m o d p )
所以2~p-2和逆元两两对应
剩下1 ∗ ( p − 1 ) ≡ − 1 ( m o d p ) 1*(p-1) \equiv -1 (mod \space p) 1 ∗ ( p − 1 ) ≡ − 1 ( m o d p )
中国剩余定理
求解x,满足方程组
\bbox[yellow]{
\begin{cases}
x \equiv 2 & \text{(mod 3)} \\
x \equiv 3 & \text{(mod 5)} \\
x \equiv 2 & \text{(mod 7)} \\
\end{cases}
}
令M = m 1 m 2 … m n , M i = M / m i M=m_1m_2\dots m_n,M_i=M/m_i M = m 1 m 2 … m n , M i = M / m i
显然有( M i , m i ) = 1 (M_i,m_i)=1 ( M i , m i ) = 1 ,所以M i M_i M i 关于模m i m_i m i 的逆元存在,把这个逆元设为t i t_i t i
于是有M i t i ≡ 1 (mod m) , M i t i ≡ 0 ( m o d m j ) ( j ≠ i ) M_it_i \equiv 1 \text{(mod m)},M_it_i \equiv 0 (mod \space m_j)(j \not= i) M i t i ≡ 1 (mod m) , M i t i ≡ 0 ( m o d m j ) ( j = i )
进一步有a i M i t i ≡ a i ( m o d m i ) , a i M i t i ≡ 0 ( m o d m j ) ( j ≠ i ) a_iM_it_i \equiv a_i(mod \space m_i),a_iM_it_i \equiv 0 (mod \space m_j)(j \not= i) a i M i t i ≡ a i ( m o d m i ) , a i M i t i ≡ 0 ( m o d m j ) ( j = i )
因此解为x ≡ a 1 M 1 t 1 + a 2 M 2 t 2 + … … a n M n t n ( m o d M ) x \equiv a_1M_1t_1+a_2M_2t_2+\dots \dots a_nM_nt_n(mod \space M) x ≡ a 1 M 1 t 1 + a 2 M 2 t 2 + … … a n M n t n ( m o d M ) ,且在模m意义下唯一解
拓展中国剩余定理
求解x,满足方程组x ≡ a i ( m o d m i ) x \equiv a_i(mod \space m_i) x ≡ a i ( m o d m i ) ,其中m i m_i m i 不一定两两互质
同样的,在mod l c m ( m i ) lcm(m_i) l c m ( m i ) 的意义下有唯一解
考虑如何每次将两个方程$\begin{cases} x \equiv a_1 \space mod \space m_1 \x \equiv a_2 \space mod \space m_2\end{cases} 合并成一个方程 合并成一个方程 合 并 成 一 个 方 程 x \equiv a(mod \space m =lcm(m_1,m_2))
\begin{cases} x\equiv a_1(mod \space m_1) \
x\equiv a_2(mod \space m_2)\end{cases} \iff
\begin{cases} x=a_1+k_1m_1 \
x=a_2+k_2m_2\end{cases}
\iff k_1m_1-k_2m_2=a_2-a_1
先用拓展欧几里得算法求出
先用拓展欧几里得算法求出 先 用 拓 展 欧 几 里 得 算 法 求 出 k_1m_1-k_2m_2=gcd(m_1,m_2)的一组特解 的一组特解 的 一 组 特 解 K_1,K_2$
$ \begin{cases} x=a_1+K_1 \frac {a_2-a_1} {gcd(m_1,m_2)}m_1 \ x=a_2+K_2 \frac {a_2-a_1}{gcd(m_1,m_2)}m_2 \end{cases}可以发现两个不同的解 x 的差一定是 可以发现两个不同的解x的差一定是 可 以 发 现 两 个 不 同 的 解 x 的 差 一 定 是 lcm(m_1,m_2)$的倍数
所以x ≡ a 1 + K 1 a 2 − a 1 g c d ( m 1 , m 2 ) = a 2 + K 2 a 2 − a 1 g c d ( m 1 , m 2 ) m 2 ( m o d l c m ( m 1 , m 2 ) ) x \equiv a_1+K_1 \frac{a_2-a_1}{gcd(m_1,m_2)}=a_2+K_2 \frac {a_2-a_1}{gcd(m_1,m_2)}m_2(mod \space lcm(m_1,m_2)) x ≡ a 1 + K 1 g c d ( m 1 , m 2 ) a 2 − a 1 = a 2 + K 2 g c d ( m 1 , m 2 ) a 2 − a 1 m 2 ( m o d l c m ( m 1 , m 2 ) )
其他常见的积性函数
μ ( n ) = { 0 , 0有平方因子 ( − 1 ) k n无平方因子,k为n的质因数个数
\mu(n)=\begin{cases} 0,&\text{0有平方因子} \\(-1)^k &\text{n无平方因子,k为n的质因数个数} \end{cases}
μ ( n ) = { 0 , ( − 1 ) k 0 有平方因子 n 无平方因子, k 为 n 的质因数个数
σ k ( n ) = ∑ i ∣ n i k \sigma_k(n)=\sum_{i|n}i^k σ k ( n ) = ∑ i ∣ n i k ,σ 0 ( n ) \sigma_0(n) σ 0 ( n ) 又写作d ( n ) d(n) d ( n ) 表示n的约数个数(除数函数)
g c d ( n , k ) gcd(n,k) g c d ( n , k )
完全积性函数:
1 ( n ) = 1 , i d k ( n ) = n k , ϵ ( n ) = { 1 , n = 1 0 , n −̸ 1 1(n)=1,id_k(n)=n^k,\epsilon(n)=\begin{cases} 1,n=1 \\0,n \not-1\end{cases} 1 ( n ) = 1 , i d k ( n ) = n k , ϵ ( n ) = { 1 , n = 1 0 , n − 1
N δ = ∏ p p ⌊ 1 p δ − 1 ⌋ , N δ N_{\delta}= \prod_pp^{\lfloor \frac 1 {p^{\delta}-1} \rfloor},N_{\delta} N δ = ∏ p p ⌊ p δ − 1 1 ⌋ , N δ 显然收敛
当n > N δ n>N_{\delta} n > N δ 时,有:d ( n ) < n ( 1 + δ ) l o g 2 l o g l o g n d(n)<n^{(1+\delta)\frac {log2}{log\space logn}} d ( n ) < n ( 1 + δ ) l o g l o g n l o g 2
普通筛法
以下整数,代指>1的整数
基本思想:整数的整数倍是合数,时间复杂度O ( n l o g n ) O(nlogn) O ( n l o g n )
1 2 3 4 5 6 7 for (int i=2 ;i<=n;i++){ if (!vis[i]) p[++t]=i; for (int j=2 *i;j<=n;j+=i) vis[j]=1 ; }
埃氏筛
基本思想:素数的整数倍数为合数,时间复杂度O ( n l o g l o g n ) O(n\space log \space logn) O ( n l o g l o g n )
1 2 3 4 5 6 7 8 9 for (int i=2 ;i<=n;i++){ if (!vis[i]) { p[++t]=i; for (int j=2 *i;j<=n;j+=i) vis[j]=true ; } }
线性筛
基本思想:整数的素数倍数是合数
枚举整数的素数倍数,当枚举到的素数是当前整数的因数时候停止
设n的最小值因子为p n p_n p n ,n会且仅在i = n / p n i=n/p_n i = n / p n 倍筛到
1 2 3 4 5 6 7 8 9 10 11 for (int i=2 ;i<=n;i++){ if (!vis[i]) p[++t]=i; for (int j=1 ;j<=t&&i*p[j]<=n;j++) { vis[i*p[j]]=1 ; if (i%p[j]==0 ) break ; } }
区间筛
求[ a , b ] [a,b] [ a , b ] 中的所有质数,a , b ≤ 1 0 12 , b − a ≤ 1 0 6 a,b \leq 10^{12},b-a \leq 10^6 a , b ≤ 1 0 1 2 , b − a ≤ 1 0 6
基本思想:埃氏筛
1.先筛出1 到 b 1到 \sqrt{b} 1 到 b 的所有质数
2.用这些质数筛去[ a , b ] [a,b] [ a , b ] 中的合数
线性筛求积性函数
对于每个合数n,在线性筛的过程中都会拆成n p n \frac n {p_n} p n n 和p n p_n p n
设( p n inf , n ) = p n k (p_n^{\inf},n)=p_n^k ( p n i n f , n ) = p n k ,则n p n k \frac n {p_n^k} p n k n 和p n k p_n^k p n k 是互质的
1.算出f ( p n k ) f(p_n^k) f ( p n k )
2.对于其他合数,f ( n ) = f ( n p n k ) ∗ f ( p n k ) f(n)=f(\frac n {p_n^k})*f(p_n^k) f ( n ) = f ( p n k n ) ∗ f ( p n k )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 for (int i=2 ;i<=n;i++){ if (!vis[i]) { p[++t]=i; phi[i]=i-1 ; } for (int j=1 ;j<=t&&i*p[j]<=n;j++) { vis[i*p[j]]=true ; if (i%p[j]) phi[i*p[j]]=phi[i]*(p[j]-1 ); else { phi[i*p[j]]=phi[i]*p[j]; break ; } } }
P2303 Longge的问题
思路
1.直接计算可以得65分,非常良心
我们设 t ( x ) t(x) t ( x ) 为 1 − n 1 - n 1 − n 中与 n n n 的最大公因数为 x x x 的数的个数,即
t ( x ) = ∑ i = 1 n [ gcd ( i , n ) = x ] = ∑ i = 1 ⌊ n x ⌋ [ gcd ( i , ⌊ n x ⌋ ) = 1 ] t(x) = \sum_{i = 1}^n[\gcd(i, n) = x] = \sum_{i = 1}^{\lfloor\frac nx\rfloor}[\gcd(i, \lfloor\frac nx\rfloor) = 1] t ( x ) = ∑ i = 1 n [ g cd( i , n ) = x ] = ∑ i = 1 ⌊ x n ⌋ [ g cd( i , ⌊ x n ⌋ ) = 1 ]
考虑 ϕ ( x ) = ∑ i = 1 n [ g c d ( i , n ) = 1 ] \phi(x) = \sum\limits_{i = 1}^n[gcd(i, n) = 1] ϕ ( x ) = i = 1 ∑ n [ g c d ( i , n ) = 1 ]
即 t ( x ) = ϕ ( ⌊ n x ⌋ ) t(x) = \phi(\lfloor\frac nx\rfloor) t ( x ) = ϕ ( ⌊ x n ⌋ )
由于gcd ( i , n ) ∣ n \gcd(i, n) | n g cd( i , n ) ∣ n ,考虑化简原式为:
∑ i = 1 n gcd ( i , n ) = ∑ d ∣ n d ∑ i = 1 n [ gcd ( i , n ) = d ] = ∑ d ∣ n d ϕ ( n d ) \sum_{i = 1}^n\gcd(i, n) = \sum_{d|n}d\sum_{i = 1}^n[\gcd(i, n) = d] = \sum_{d|n}d\phi(\frac nd) ∑ i = 1 n g cd( i , n ) = ∑ d ∣ n d ∑ i = 1 n [ g cd( i , n ) = d ] = ∑ d ∣ n d ϕ ( d n )
对于所有 d ∣ n d | n d ∣ n 可以在 O ( n ) O(\sqrt n) O ( n ) 的时间复杂度内枚举,ϕ \phi ϕ 函数可以在 O ( n ) O(\sqrt n) O ( n ) 的时间复杂度内求出,故时间复杂度为 O ( 因数个数 ∗ n ) O(因数个数 * \sqrt n) O ( 因 数 个 数 ∗ n )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> using namespace std;long long n;int main () { scanf ("%lld" ,&n); long long ans=n; for (register long long i=2 ;i*i<=n;++i) { if (n%i==0 ) { int b=0 ; while (n%i==0 ) { b++; n/=i; } ans/=i; ans*=b*i-b+i; } } if (n>1 ) { ans/=n; ans*=n+n-1 ; } printf ("%lld" ,ans); return 0 ; }
CF757B Bash’s Big Day
思路
g c d > 1 gcd>1 g c d > 1 ,说明存在一个正整数d > 1 d>1 d > 1 ,满足d整除s内的所有元素
枚举d = 2 → m a x a i d=2 \to max_{a_i} d = 2 → m a x a i 并统计答案
设V = m a x a i V=max_{a_i} V = m a x a i ,则复杂度为O ( V l n V ) O(VlnV) O ( V l n V )
1 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> using namespace std;const int maxn=100005;int n,tot,maxx=1;int a[maxn],fac[maxn];inline int read(){ int x=0,f=1;char ch=getchar(); while (ch<'0'||ch> '9' ){if (ch=='-' ) f=-1;ch=getchar();} while (ch>='0' &&ch<='9' ){x=x*10+ch-48;ch=getchar();} return x*f;}void record(int x){ for(int i=2;i*i<=x;i++) { if (x%i==0) { fac[i]++; fac[x/i]++; } if (i*i==x) fac[x/i]--; } fac[x]++; return ;}int main(){ n=read(); for(int i=1;i<=n;i++) { a[i]=read(); record(a[i]); } sort(a+1,a+1+n); for(int i=2;i<=a[n];i++) maxx=max(maxx,fac[i]); cout<<maxx<<endl; return 0;}
CF776B Sherlock and his girlfriend
思路
注意到这是二分图,一边是质数,一边是合数
运用埃氏筛把合数都筛掉
然后质数和合数的颜色不同就可以了
1 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> using namespace std;int n;bool p[100005];int main(){ cin>>n; p[0]=true,p[1]=true; for(int i=2;i*i<=n+1;i++) { if (!p[i]) { for(int j=i*2;j<=n+1;j+=i) p[j]=true; } } if (n<3) cout<<1<<'\n' ; else cout<<2<<'\n' ; for(int i=2;i<=n+1;i++) cout<<(p[i]? 1:0)+1<<" " ; cout<<'\n' ; return 0;}
P4139 上帝与集合的正确用法
思路
题目要求出的是2 2 2 2 … m o d p 2^{2^{2^{2…}}} \space mod \space p 2 2 2 2 … m o d p 的值
根据扩展欧拉定理
a b ≡ { a b mod φ ( p ) + φ ( p ) ( b > φ ( p ) ) a b ( b ≤ φ ( p ) ) (mod p )
a^b\equiv \left\{\begin{aligned}a^{b \text{ mod }φ(p)+φ(p)}(b&>φ(p))\\a^b(b≤φ(p))\end{aligned}\right. \text{ (mod }p)
a b ≡ { a b mod φ ( p ) + φ ( p ) ( b a b ( b ≤ φ ( p ) ) > φ ( p ) ) (mod p )
可以通过一个函数来递归求
可是题目中让求的是无限层,并不是有限层,怎么办啊?
不要紧,在一层层的递归中,p p p 一遍遍的被求其φ φ φ 值,最终必定 会降到1 1 1 。原因很简单,因为:φ ( p ) < p ( p > 1 ) φ(p)<p\space(p>1) φ ( p ) < p ( p > 1 )
而且降的很快,所以递归很快就会结束
1 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> using namespace std;const int maxn=1e7+5;int T,tot;int pri[1000005],phi[maxn];bool vis[maxn];inline int read(){ int x=0,f=1;char ch=getchar(); while (ch<'0'||ch> '9' ){if (ch=='-' ) f=-1;ch=getchar();} while (ch>='0' &&ch<='9'){x=x*10+ch-48;ch=getchar();} return x*f;}int ksm(int a,int b,int p){ int ans=1%p; for( ;b;b> >=1) { if (b&1) ans=(long long)ans*a%p; a=(long long)a*a%p; } return ans;}int dfs(int x){ if (x==1) return 0; int n=phi[x]; return ksm(2,dfs(n)+n,x);}void init(){ for(int i=2;i<=maxn;i++) { if (!vis[i]) pri[++tot]=i,phi[i]=i-1; for(int j=1;j<=tot&&pri[j]*i<=maxn;j++) { vis[pri[j]*i]=1; if (i%pri[j]) phi[i*pri[j]]=phi[i]*phi[pri[j]]; else { phi[i*pri[j]]=phi[i]*pri[j]; } } }}int main(){ init(); T=read(); while(T--) cout<<dfs(read())<<'\n' ; return 0;}
一道例题
题目
求[ 1 , 1 0 7 ] [1,10^7] [ 1 , 1 0 7 ] 内的所有素数,内存限制1MB
思路
吧[ 1 , 1 0 7 ] [1,10^7] [ 1 , 1 0 7 ] 分成k段分别求解
对于区间[ l , r ] [l,r] [ l , r ] ,枚举不大于r \sqrt{r} r 的所有素数p,在[ l , r ] [l,r] [ l , r ] 中筛去p的倍数
需要预处理[ 1 , n ] [1,\sqrt{n}] [ 1 , n ] 的所有素数
时间复杂度k n + n l o g l o g n k\sqrt{n}+nlog^{log^n} k n + n l o g l o g n
空间复杂度n + n / k \sqrt{n}+n/k n + n / k
P2568 gcd
思路
线性筛出不大于N N N 的所有素数p p p 。问题就转化为求( x , y ) = p (x,y)=p ( x , y ) = p 的个数
设x = x ′ p , y = y ′ p x=x'p,y=y'p x = x ′ p , y = y ′ p 那么有( x ′ , y ′ ) = 1 (x',y')=1 ( x ′ , y ′ ) = 1 且1 ≤ x , y ≤ N / p 1 \le x,y \le N/p 1 ≤ x , y ≤ N / p
转化为求( x , y ) = 1 (x,y)=1 ( x , y ) = 1 且1 ≤ x , y ≤ n 1 \le x,y \le n 1 ≤ x , y ≤ n 的个数
答案为2 ∑ i = 1 n ϕ ( i ) − 1 2\sum_{i=1}^n\phi(i)-1 2 ∑ i = 1 n ϕ ( i ) − 1
线性筛筛出欧拉函数,预处理前缀和就可以了
然后对于每个小于等于N N N 的素数处理累加到答案里就可以了
1 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> #define int long longusing namespace std;const int maxn=1e7+5;int pri[maxn],phi[maxn],s[maxn];bool vis[maxn];int n,tot;long long ans=0;void init(int x){ phi[1]=1; for(int i=2;i<=x;i++) { if(!vis[i]) pri[++tot]=i,phi[i]=i-1; for(int j=1;j<=tot&&pri[j]*i<=x;j++) { vis[pri[j]*i]=1; if(i%pri[j]) phi[i*pri[j]]=phi[i]*phi[pri[j]]; else { phi[i*pri[j]]=phi[i]*pri[j]; } } } for(int i=1;i<=x;i++) s[i]=s[i-1]+phi[i];} signed main(){ cin> >n; init(n); for(int i=1;i<=tot;i++) ans+=(2*s[n/pri[i]]-1); cout<<ans<<'\n' ; return 0;}
P2155 沙拉公主的困惑
思路
感谢@Siyuan
PS:越来越意识到开long long的重要性
答案是:
∑ i = 1 n ! [ g c d ( i , m ! ) = 1 ]
\sum_{i=1}^{n!}[gcd(i,m!)=1]
∑ i = 1 n ! [ g c d ( i , m ! ) = 1 ]
我们类比gcd
我们知道n ≥ m n \ge m n ≥ m ,所以我们可以把[ 1 , n ! ] [1,n!] [ 1 , n ! ] 分成若干段
准确地来说是 n ! m ! \dfrac {n!} {m!} m ! n ! 段,每段的答案是一样的,这是由gcd的更相减损术得到的
然后我们就可以更换求和的上界
n ! m ! ∑ i = 1 m ! [ g c d ( i , m ! ) = 1 ]
\dfrac {n!} {m!}\sum_{i=1}^{m!}[gcd(i,m!)=1]
m ! n ! ∑ i = 1 m ! [ g c d ( i , m ! ) = 1 ]
右边的就是1到m!中与m互质的数,也就是
φ ( m ! )
\varphi(m!)
φ ( m ! )
我们知道,设n = ∏ p i k i n =\prod p_i^{k_i} n = ∏ p i k i ,则φ ( n ) = n ∏ ( 1 − 1 p i ) = ∏ p i k i − 1 ∗ ( p i − 1 ) \varphi(n)=n \prod(1-\frac 1 {p_i})=\prod p_i^{k_i-1}*(p_i-1) φ ( n ) = n ∏ ( 1 − p i 1 ) = ∏ p i k i − 1 ∗ ( p i − 1 )
所以我们设m ! = ∏ i = 1 k p i c i m!=\prod_{i=1}^kp_i^{c_i} m ! = ∏ i = 1 k p i c i ,可以得到:
φ ( m ! ) = m ! ∏ i = 1 k p i − 1 p i
\varphi(m!)=m! \prod_{i=1}^k \dfrac {p_i-1} {p_i}
φ ( m ! ) = m ! ∏ i = 1 k p i p i − 1
所以讲这个东西带入上面的式子,答案就是
n ! ∏ i = 1 k p i − 1 p i
n!\prod_{i=1}^k \dfrac {p_i-1} {p_i}
n ! ∏ i = 1 k p i p i − 1
那个玩意儿我们可以用线性筛求
时间复杂度O ( N + T ) O(N+T) O ( N + T )
1 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> #define int long longusing namespace std;const int maxn=1e7+5;int T,R;int tot,pri[maxn],fac[maxn],inv[maxn];int ans[maxn];bool vis[maxn];void init(){ fac[0]=fac[1]=1; inv[0]=inv[1]=1; ans[0]=ans[1]=1; for(int i=2;i<=maxn-5;i++) { fac[i]=(long long)(fac[i-1]*i)%R;//算阶乘 inv[i]=(long long)((R-R/i)*inv[R%i])%R;//逆元 } //欧拉筛 for(int i=2;i<=maxn-5;i++) { if(!vis[i]) pri[++tot]=i; for(int j=1;j<=tot&&i*pri[j]<=maxn-5;j++) { vis[i*pri[j]]=1; if(i%pri[j]==0) break; } } for(int i=2;i<=maxn-5;i++) { ans[i]=ans[i-1]; if(!vis[i]) ans[i]=(long long)(ans[i]*(i-1)%R*inv[i]%R)%R; }}signed main(){ cin> >T>>R; init(); while(T--) { int n,m; cin>>n>>m; cout<<(long long)fac[n]*ans[m]%R<<'\n' ; } return 0;}
CF632D Longest Subsequence
思路
我们要选出尽可能多的数字,满足他们的最小公倍数不大于m
分析题意可以得到,如果一个数大于m的话那么它和其他数的最小公倍数一定大于m
所以先把大于m的数字筛掉
然后
1 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> using namespace std;const int maxn=1000005;int n,m;int a[maxn],ans[maxn];int cnt[maxn];bool vis[maxn];int main(){ std::ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++) { cin> >a[i]; if (a[i]<=m) cnt[a[i]]++; } for(int i=1;i<=n;i++) { if (a[i]<=m&&!vis[a[i]]) { vis[a[i]]=true; for(int j=1;j*a[i]<=m;j++) ans[a[i]*j]+=cnt[a[i]]; } } int maxx=-9999999; int lcm=1; for(int i=1;i<=m;i++) { if (maxx<ans[i]) { maxx=ans[i]; lcm=i; } } cout<<lcm<<" " <<maxx<<'\n' ; for(int i=1;i<=n;i++) { if (lcm%a[i]==0) cout<<i<<" " ; } cout<<'\n' ; return 0;}
CF582A GCD Table
思路
为啥CF的题目就是这么妙呢?
是我不配了
观察得到三个结论
1.G中最大的数也一定是a中最大的数,因为g c d ( a , a ) = a gcd(a,a)=a g c d ( a , a ) = a ,如果它是最大的话,也一定是最大的
2.G中次大的数也一定是a中次大的数
3.第三第四大的数可能是由最大和次大的GCD产生的;
所以算法就有这样的步骤
1.令p为G中最大的数,在G中删掉p,在a中加入p
2.对于a中的所有其他数,设为q,在G中删除两个g c d ( p , q ) gcd(p,q) g c d ( p , q )
3.若G为空则结束,否则回到1
则时间复杂度为O ( n 2 l o g G i , j ) O(n^2logG_{i,j}) O ( n 2 l o g G i , j )
因为值域到1 0 9 10^9 1 0 9 ,所以要用map处理
1 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> #include <map> using namespace std;const int maxn=505;int n,cnt,tot;int arr[maxn],a[maxn*maxn];map <int,int> m;int gcd(int a,int b){ return b? gcd(b,a%b):a;}int main(){ cin>>n; for(int i=1;i<=n*n;i++) { int x; cin> >x; if (!m[x]) a[++cnt]=x; m[x]++; }