线性筛
给定一个范围 n n n ,有 q q q 个询问,每次输出 k k k 小的素数。
数据范围 n = 1 0 8 , 1 ≤ q ≤ 1 0 6 n=10^8,1\le q\le 10^6 n = 1 0 8 , 1 ≤ q ≤ 1 0 6 ,保证询问的素数不大于 n n n 。
题目链接:P3383 。
简单说一下线性筛的原理。
枚举当前已经筛出来的质数,设当前筛到的数字是 i i i ,第 j j j 个质数是 p j p_j p j ,那么有:
当 i m o d p j ≠ 0 i\bmod p_j \ne 0 i mod p j = 0 ,由于 p j p_j p j 是从小到大枚举,且 p j p_j p j 比 i i i 的最小质因子还小,所以 p j p_j p j 一定是 i × p j i\times p_j i × p j 的最小质因子。
当 i m o d p j = 0 i\bmod p_j=0 i mod p j = 0 ,此时 p j p_j p j 是 i i i 的最小质因子,标记完 i × p j i\times p_j i × p j 后就跳出循环,因为如果继续枚举就不一定是最小质因子了。
这样保证了每个数字只会被筛一次,也就保证了线性的复杂度。
枚举到 i i i 时,[ 1 , i ] [1,i] [ 1 , i ] 间的所有质数一定已经被筛出来了,因为如果一个数不是质数,它一定含有一个比它小的质因子,就会被前面的步骤筛掉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <bits/stdc++.h> using namespace std;const int N = 1e8 +10 ;int n, q, k;bitset<N> st; vector<int > primes; void init (int n) { for (int i = 2 ; i <= n; i++) { if (!st[i]) primes.emplace_back (i); for (int j = 0 ; primes[j] <= n/i; j++) { st[i*primes[j]] = 1 ; if (i % primes[j] == 0 ) break ; } } } int main () { scanf ("%d%d" , &n, &q); init (n); while (q--) scanf ("%d" , &k), printf ("%d\n" , primes[k-1 ]); return 0 ; }
素数密度
给定区间 [ L , R ] [L,R] [ L , R ] (1 ≤ L ≤ R < 2 31 1\leq L\leq R < 2^{31} 1 ≤ L ≤ R < 2 31 ,R − L ≤ 1 0 6 R-L\leq 10^6 R − L ≤ 1 0 6 ),请计算区间中素数的个数。
输入格式
第一行,两个正整数 L L L 和 R R R 。
输出格式
一行,一个整数,表示区间中素数的个数。
题目链接:P1835 。
如果一个数 x x x 是合数,一定存在一个小于等于 x \sqrt x x 的质因数,所以我们把 2 31 < 50000 \sqrt {2^{31}}<50000 2 31 < 50000 的质数全部筛出来,然后就可以判断区间 L , R L,R L , R 内的合数了。
由于 1 1 1 不会被筛掉,而 1 1 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 #include <bits/stdc++.h> using namespace std;const int N = 50010 , M = 1000010 ;int l, r;vector<int > primes; bitset<N> st; bitset<M> pst; void init (int n) { for (int i = 2 ; i <= n; i++) { if (!st[i]) primes.emplace_back (i); for (int j = 0 ; primes[j] <= n/i; j++) { st[i*primes[j]] = 1 ; if (i % primes[j] == 0 ) break ; } } } int main () { scanf ("%d%d" , &l, &r); init (N-1 ); int tot = r-l+1 ; for (int p: primes) { for (long long i = max ((l+p-1 )/p * p, 2 *p); i <= r; i += p) { if (pst[i-l]) continue ; pst[i-l] = 1 ; tot--; } } if (l == 1 ) tot--; return !printf ("%d\n" , tot); }
[AHOI2005] 约数研究
求 ∑ i = 1 n d ( i ) \sum_{i=1}^n d(i) ∑ i = 1 n d ( i ) ,其中 d ( i ) d(i) d ( i ) 表示 i i i 的约数个数,n ≤ 1 0 6 n\le 10^6 n ≤ 1 0 6 。
题目链接:P1403 。
推下式子。
∑ i = 1 n d ( i ) = ∑ i = 1 n ∑ j = 1 n [ j ∣ i ] = ∑ j = 1 n ∑ i = 1 n [ j ∣ i ] = ∑ j = 1 n ⌊ n j ⌋ \begin{aligned}
\sum_{i=1}^n d(i) &= \sum_{i=1}^n\sum_{j=1}^n[j|i]\\
&=\sum_{j=1}^n\sum_{i=1}^n [j|i]\\
&=\sum_{j=1}^n\lfloor\frac{n}{j}\rfloor
\end{aligned}
i = 1 ∑ n d ( i ) = i = 1 ∑ n j = 1 ∑ n [ j ∣ i ] = j = 1 ∑ n i = 1 ∑ n [ j ∣ i ] = j = 1 ∑ n ⌊ j 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 #include <bits/stdc++.h> using namespace std;int n;int main () { scanf ("%d" , &n); int res = 0 ; for (int i = 1 ; i <= n; i++) { int j = n / (n / i); res += (j-i+1 ) * (n / i); i = j; } return !printf ("%d\n" , res); }
[洛谷月赛] 欧拉函数求和
给定 l , r l,r l , r 求:
∑ i = l r i − φ ( i ) mod 666623333 \sum_{i=l}^r i-\varphi(i) \text{ mod } 666623333
i = l ∑ r i − φ ( i ) mod 666623333
其中 1 ≤ l ≤ r ≤ 1 0 12 , r − l ≤ 1 0 6 1\le l\le r\le 10^{12}, r-l\le 10^6 1 ≤ l ≤ r ≤ 1 0 12 , r − l ≤ 1 0 6 。
题目链接:P3601 。
这也是区间筛的一个例子,我们先用 1 0 12 \sqrt{10^{12}} 1 0 12 以内的数枚举掉 [ l , r ] [l,r] [ l , r ] 每一个数的质因子,对于大于 1 0 12 \sqrt {10^{12}} 1 0 12 的因子特判一下即可。
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 #include <bits/stdc++.h> using namespace std;const int N = 1000010 , P = 666623333 ;typedef long long LL;LL l, r; LL phi[N], remain[N]; bitset<N> st; vector<int > primes; void init (int n) { for (int i = 2 ; i <= n; i++) { if (!st[i]) primes.emplace_back (i); for (int j = 0 ; primes[j] <= n/i; j++) { st[i*primes[j]] = 1 ; if (i % primes[j] == 0 ) break ; } } } int main () { init (N-1 ); scanf ("%lld%lld" , &l, &r); for (LL i = l; i <= r; i++) phi[i-l] = remain[i-l] = i; for (int p: primes) { for (LL i = (l+p-1 )/p*p; i <= r; i += p) { bool flag = false ; while (remain[i-l] % p == 0 ) { remain[i-l] /= p; flag = true ; } if (flag) phi[i-l] -= phi[i-l] / p; } } LL res = 0 ; for (LL i = l; i <= r; i++) { if (remain[i-l] > 1 ) phi[i-l] -= phi[i-l] / remain[i-l]; res = (res + i - phi[i-l]) % P; } return !printf ("%lld\n" , res); }
[UR #3] 次大公约数
设 sgcd ( x , y ) \text{sgcd}(x,y) sgcd ( x , y ) 为 x , y x,y x , y 的次大公约数,如果不存在则 sgcd ( x , y ) = − 1 \text{sgcd}(x,y)=-1 sgcd ( x , y ) = − 1 ,给定长度为 n n n 的序列 a a a ,输出 sgcd ( a 1 , a 1 ) , sgcd ( a 1 , a 2 ) , ⋯ , sgcd ( a 1 , a n ) \text{sgcd}(a_1,a_1),\text{sgcd}(a_1,a_2),\cdots,\text{sgcd}(a_1,a_n) sgcd ( a 1 , a 1 ) , sgcd ( a 1 , a 2 ) , ⋯ , sgcd ( a 1 , a n ) 。
数据范围 1 ≤ n ≤ 1 0 5 , 1 ≤ a i ≤ 1 0 12 1\le n\le 10^5, 1\le a_i\le 10^{12} 1 ≤ n ≤ 1 0 5 , 1 ≤ a i ≤ 1 0 12
题目链接:UOJ48 。
显然 sgcd ( a , b ) \text{sgcd}(a,b) sgcd ( a , b ) 是 gcd ( a , b ) \gcd(a,b) g cd( a , b ) 除掉最小的那个质因数。
我们考虑题目反复用到了 a 1 a_1 a 1 ,而 gcd \gcd g cd 的式子从质因数分解的角度来看应该是这样的:
gcd ( ∏ p i x i , ∏ p i y i ) = ∏ p i min ( x i , y i ) \gcd(\prod p_i^{x_i},\prod p_i^{y_i})=\prod p_i^{\min(x_i,y_i)}
g cd( ∏ p i x i , ∏ p i y i ) = ∏ p i m i n ( x i , y i )
所以,我们先把 a 1 a_1 a 1 分解,gcd \gcd g cd 中的质因子一定包含于 a 1 a_1 a 1 的质因子,而质因子的数量是比 log \log log 级别还要低的,所以这题就能过了。
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 #include <bits/stdc++.h> using namespace std;typedef long long LL;const int N = 100010 , M = 1000010 ;int n;LL a[N]; vector<LL> factors; LL gcd (LL a, LL b) { if (!b) return a; return gcd (b, a % b); } LL sgcd (LL b) { LL g = gcd (a[1 ], b); if (g == 1 ) return -1 ; for (LL f: factors) if (g % f == 0 ) return g / f; return -2 ; } void factorize (LL x) { for (int i = 2 ; i <= x/i; i++) { bool flag = false ; while (x % i == 0 ) { x /= i; flag = true ; } if (flag) factors.emplace_back (i); } if (x > 1 ) factors.emplace_back (x); } int main () { scanf ("%d" , &n); for (int i = 1 ; i <= n; i++) scanf ("%lld" , &a[i]); factorize (a[1 ]); for (int i = 1 ; i <= n; i++) printf ("%lld " , sgcd (a[i])); return 0 ; }
[HAOI2008] 圆上的整点
给定 r ≤ 2 × 1 0 9 r\le 2\times 10^9 r ≤ 2 × 1 0 9 ,求圆 x 2 + y 2 = r 2 x^2+y^2=r^2 x 2 + y 2 = r 2 上有多少个整点。
题目链接:P2508 。
勾股方程通解,其中 gcd ( u , v ) = 1 \gcd(u,v)=1 g cd( u , v ) = 1 :
{ x = 1 2 d ( u 2 − v 2 ) y = d u v r = 1 2 d ( u 2 + v 2 ) \begin{cases}
x=\frac{1}{2}d(u^2-v^2)\\
y=duv\\
r=\frac{1}{2}d(u^2+v^2)
\end{cases}
⎩ ⎨ ⎧ x = 2 1 d ( u 2 − v 2 ) y = d uv r = 2 1 d ( u 2 + v 2 )
若 gcd ( u , v ) ≠ 1 \gcd(u,v)\ne 1 g cd( u , v ) = 1 ,我们可以乘给 d d d ,所以这样做是可以不重复地找出所有符合要求的三元组 ( d , u , v ) (d,u,v) ( d , u , v ) 。
首先枚举 d d d 满足 d ∣ 2 r d\mid 2r d ∣ 2 r ,然后枚举 u u u 满足 v 2 = 2 r d − u 2 v^2=\frac{2r}{d}-u^2 v 2 = d 2 r − u 2 则有 u 2 < 2 r / d u^2\lt 2r/d u 2 < 2 r / d ,检查 v v v 是否是完全平方数即可。
由于圆上的点是对称的,所以只需要关心第一象限内的点,即 x > 0 , y > 0 x>0,y>0 x > 0 , y > 0 ,然后答案乘 4 加上坐标轴上的四个点就是最终答案。
复杂度为 O ( 2 r ∑ d ∣ 2 r 1 d ) O(2r \sum_{d\mid 2r} \frac{1}{\sqrt{d}}) O ( 2 r ∑ d ∣ 2 r d 1 ) ,由于约数个数是 O ( n ) O(\sqrt n) O ( n ) 的,因此复杂度上界为 O ( r ) O(r) O ( r ) ,但是远远达不到。
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 #include <bits/stdc++.h> using namespace std;typedef long long LL;LL r; LL gcd (LL a, LL b) { if (!b) return a; return gcd (b, a % b); } bool check (LL u, LL v2) { LL v = sqrt (v2); if (v * v == v2) { return gcd (u, v) == 1 ; } return false ; } LL calc (LL d) { LL res = 0 ; for (LL u = 1 ; u * u * 2 < d; u++) { LL v2 = d - u * u; res += check (u, v2); } return res; } int main () { scanf ("%lld" , &r); LL res = 0 ; for (LL d = 1 ; d * d <= 2 * r; d++) if (2 * r % d == 0 ) res += calc (2 * r / d) + (d * d == 2 * r ? 0 : calc (d)); return !printf ("%lld\n" , res * 4 + 4 ); }
[NOIP2014] 解方程
已知多项式方程:
a 0 + a 1 x + a 2 x 2 + ⋯ + a n x n = 0 a_0+a_1x+a_2x^2+\cdots+a_nx^n=0
a 0 + a 1 x + a 2 x 2 + ⋯ + a n x n = 0
求这个方程在 [ 1 , m ] [1,m] [ 1 , m ] 内的整数解(n n n 和 m m m 均为正整数)。
输入格式
输入共 n + 2 n + 2 n + 2 行。
第一行包含 2 2 2 个整数 n , m n, m n , m ,每两个整数之间用一个空格隔开。
接下来的 n + 1 n+1 n + 1 行每行包含一个整数,依次为 a 0 , a 1 , a 2 … a n a_0,a_1,a_2\ldots a_n a 0 , a 1 , a 2 … a n 。
输出格式
第一行输出方程在 [ 1 , m ] [1,m] [ 1 , m ] 内的整数解的个数。
接下来每行一个整数,按照从小到大的顺序依次输出方程在 [ 1 , m ] [1,m] [ 1 , m ] 内的一个整数解。
对于 100 % 100\% 100% 的数据:0 < n ≤ 100 , ∣ a i ∣ ≤ 1 0 10000 , a n ≠ 0 , m < 1 0 6 0<n\le 100,|a_i|\le 10^{10000},a_n≠0,m<10^6 0 < n ≤ 100 , ∣ a i ∣ ≤ 1 0 10000 , a n = 0 , m < 1 0 6 。
题目链接:LOJ 2503 。
判断两个大数字是否相等,可以通过判断它们是否在模大质数下是否同余。这个判断方法只能断定两个数字是否不同,无法判断是否相同,但是在 A ≠ B A\ne B A = B 的情况下做到 A ≡ B ( m o d P ) A\equiv B\pmod P A ≡ B ( mod P ) 需要使得 A − B = k P A-B=kP A − B = k P 。
本题是判断大数是否和 0 0 0 相等,这种情况下还能同余的概率是 1 P \frac{1}{P} P 1 ,当然出题人可能刻意卡一些常见的质数,所以我们同时用两个不怎么常见的质数,998244853 , 1 0 9 + 9 998244853,10^9+9 998244853 , 1 0 9 + 9 就能过。
这题放洛谷不写 inline
过不了,我认为这是洛谷的问题,因为 LOJ
开不开 inline
差别只有 10ms
,洛谷直接 TLE 了。
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 #include <bits/stdc++.h> using namespace std;const int N = 110 , M = 10010 ;const int P = 998244853 , Q = 1e9 +9 ;int a[N], b[N], n, m;void read (int &t, int & r) { t = r = 0 ; int sgn = 1 ; char ch = getchar (); while (ch < '0' || ch > '9' ) { if (ch == '-' ) sgn = -1 ; ch = getchar (); } while ('0' <= ch && ch <= '9' ) { t = (t * 10LL + (ch ^ 48 )) % P; r = (r * 10LL + (ch ^ 48 )) % Q; ch = getchar (); } if (sgn == -1 ) t = P-t, r = Q-r; } bool check (int a[], int v, int p) { int x = 1 , res = 0 ; for (int i = 0 ; i <= n; i++) { res = (res + 1LL * a[i] * x) % p; x = 1LL * x * v % p; } return res == 0 ; } int main () { scanf ("%d%d" , &n, &m); for (int i = 0 ; i <= n; i++) read (a[i], b[i]); vector<int > sln; for (int i = 1 ; i <= m; i++) if (check (a, i, P) && check (b, i, Q)) sln.emplace_back (i); printf ("%d\n" , (int )sln.size ()); for (int s: sln) printf ("%d\n" , s); return 0 ; }
[CF226C] Anniversary
给定 1 ≤ l ≤ r ≤ 1 0 12 1\le l\le r\le 10^{12} 1 ≤ l ≤ r ≤ 1 0 12 ,求在 [ l , r ] [l,r] [ l , r ] 中选 k k k 个数字,使得 gcd ( f a 1 , … , f a k ) \gcd(f_{a_1},\dots,f_{a_k}) g cd( f a 1 , … , f a k ) 最大,其中 f f f 是斐波那契数列,答案对 m m m 取模。
题目链接:CF226C 。
由于 gcd ( f a , f b ) = f gcd ( a , b ) \gcd(f_a,f_b)=f_{\gcd(a,b)} g cd( f a , f b ) = f g c d ( a , b ) ,并且 f i f_i f i 单调递增,所以就是要在 [ l , r ] [l,r] [ l , r ] 内选 k k k 个数使他们的 gcd \gcd g cd 最大。
考虑枚举答案 x x x ,要想让答案成立,一定有 [ l , r ] [l,r] [ l , r ] 内 x x x 的倍数大于 k k k ,也就是:
⌊ r x ⌋ − ⌊ l − 1 x ⌋ ≥ k \lfloor\frac{r}{x}\rfloor-\lfloor\frac{l-1}{x}\rfloor\ge k
⌊ x r ⌋ − ⌊ x l − 1 ⌋ ≥ k
具体地,我们直接选 gcd ( x , 2 x , ⋯ , k x ) = x \gcd(x,2x,\cdots,kx)=x g cd( x , 2 x , ⋯ , k x ) = x ,这是因为 ∀ k ∈ N ∗ , gcd ( x , k x ) = x gcd ( 1 , k ) = x \forall k\in \N^*,\gcd(x,kx)=x\gcd(1,k)=x ∀ k ∈ N ∗ , g cd( x , k x ) = x g cd( 1 , k ) = x 。
这可以整除分块,所以复杂度为 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <bits/stdc++.h> using namespace std;#define int long long int l, r, m, k;struct Matrix { int dat[2 ][2 ]; Matrix () { memset (dat, 0 , sizeof dat); } Matrix operator *(const Matrix& mat) const { Matrix res; for (int k = 0 ; k < 2 ; k++) for (int i = 0 ; i < 2 ; i++) for (int j = 0 ; j < 2 ; j++) res.dat[i][j] = (res.dat[i][j] + dat[i][k] * mat.dat[k][j]) % m; return res; } }; Matrix qmi (Matrix a, int k) { Matrix res; res.dat[0 ][0 ] = res.dat[1 ][1 ] = 1 ; while (k) { if (k & 1 ) res = res * a; a = a * a; k >>= 1 ; } return res; } signed main () { cin >> m >> l >> r >> k; --l; int x = 1 , res = 1 ; while (x <= r) { if (l / x) x = min (l / (l / x), r / (r / x)); else x = r / (r / x); if (r/x - l/x >= k) res = x; x++; } Matrix mat; mat.dat[0 ][0 ] = mat.dat[0 ][1 ] = mat.dat[1 ][0 ] = 1 ; cout << qmi (mat, res-1 ).dat[0 ][0 ] % m << '\n' ; return 0 ; }