[Codeforces] number theory (R1600) Part.2
题单:https://codeforces.com/problemset/page/1?tags=number+theory%2C1201-1600
235A. LCM Challenge
原题指路:https://codeforces.com/problemset/problem/235/A
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
给定一个整数 n ( 1 ≤ n ≤ 1 e 6 ) n\ \ (1\leq n\leq 1\mathrm{e}6) n (1≤n≤1e6),求三个 ≤ n \leq n ≤n的正整数使得它们的 l c m \mathrm{lcm} lcm最大,输出 l c m \mathrm{lcm} lcm的最大值.
思路
显然应取尽量大的互素的数.
(1)若 n n n为奇数,显然 a n s = n ( n − 1 ) ( n − 2 ) ans=n(n-1)(n-2) ans=n(n−1)(n−2).
(2)若 n n n是偶数,显然应避开同时选 n n n和 n − 2 n-2 n−2的情况.
①若 3 ∣ n 3\mid n 3∣n,则 n , ( n − 1 ) , ( n − 3 ) n,(n-1),(n-3) n,(n−1),(n−3)中 gcd ( n , n − 3 ) ≥ 3 \gcd(n,n-3)\geq 3 gcd(n,n−3)≥3,应取 a n s = ( n − 1 ) ( n − 2 ) ( n − 3 ) ans=(n-1)(n-2)(n-3) ans=(n−1)(n−2)(n−3).
②若 3 ∤ n 3\not\mid n 3∣n,则 a n s = n ( n − 1 ) ( n − 3 ) ans=n(n-1)(n-3) ans=n(n−1)(n−3).
注意特判 n ≤ 3 n\leq 3 n≤3的情况.
代码
void solve() {
int n; cin >> n;
if (n == 1) cout << 1;
else if (n == 2) cout << 2;
else if (n == 3) cout << 6;
else if (n & 1) cout << (ll)n * (n - 1) * (n - 2);
else if (n % 3 == 0) cout << (ll)(n - 1) * (n - 2) * (n - 3);
else cout << (ll)n * (n - 1) * (n - 3);
}
int main() {
solve();
}
236B. Easy Number Challenge
原题指路:https://codeforces.com/problemset/problem/236/B
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
对正整数 n n n,定义 d ( n ) d(n) d(n)为 n n n的约数的个数.给定 a , b , c ( 1 ≤ a , b , c ≤ 100 ) a,b,c\ \ (1\leq a,b,c\leq 100) a,b,c (1≤a,b,c≤100),求 ∑ i = 1 a ∑ j = 1 b ∑ k = 1 c d ( i j k ) \displaystyle\sum_{i=1}^a \sum_{j=1}^b \sum_{k=1}^c d(ijk) i=1∑aj=1∑bk=1∑cd(ijk),答案对 2 30 2^{30} 230取模.
思路
预处理出 i j k ≤ 1 e 6 ijk\leq 1\mathrm{e}6 ijk≤1e6内的 d ( i j k ) d(ijk) d(ijk),直接统计答案即可.
代码
const int MAXN = 1e6 + 5;
const int MOD = 1 << 30;
int primes[MAXN], cnt;
bool state[MAXN];
int nums[MAXN]; // 数的素因子个数
int d[MAXN]; // 约数个数
void init() {
d[1] = 1;
for (int i = 2; i < MAXN; i++) {
if(!state[i]) {
state[i] = true;
primes[cnt++] = i;
nums[i] = 1;
d[i] = 2; // 素数
}
for (int j = 0; (ll)primes[j] * i < MAXN; j++) {
state[primes[j] * i] = true;
if (i % primes[j] == 0) {
nums[primes[j] * i] = nums[i] + 1;
d[primes[j] * i] = d[i] / nums[primes[j] * i] * (nums[primes[j] * i] + 1);
break;
}
else {
nums[primes[j] * i] = 1;
d[primes[j] * i] = d[i] * 2;
}
}
}
}
void solve() {
init();
int a, b, c; cin >> a >> b >> c;
ll ans = 0;
for (int i = 1; i <= a; i++) {
for (int j = 1; j <= b; j++) {
for (int k = 1; k <= c; k++)
ans += d[i * j * k];
}
}
cout << ans;
}
int main() {
solve();
}
237C. Primes on Interval
原题指路:https://codeforces.com/problemset/problem/237/C
题意
给定三个整数 a , b , k ( 1 ≤ a , b , k ≤ 1 e 6 , a ≤ b ) a,b,k\ \ (1\leq a,b,k\leq 1\mathrm{e}6,a\leq b) a,b,k (1≤a,b,k≤1e6,a≤b).求一个最小的整数$l\ \ (1\leq l\leq b-a+1)\ s.t.\ 对 对 对\forall x\in[a,b-l+1] , 在 ,在 ,在x,x+1,\cdots,x+l-1 中至少有 中至少有 中至少有k 个素数 , 若无解输出 个素数,若无解输出 个素数,若无解输出-1$.
思路
预处理出 1 e 6 1\mathrm{e}6 1e6内的素数,并预处理出素数个数的前缀和 p r e [ ] pre[] pre[].二分 l l l的值即可.
代码
const int MAXN = 1e6 + 5;
int a, b, k;
bool not_prime[MAXN];
int pre[MAXN]; // 素数的个数
void init() {
not_prime[1] = true;
for (int i = 2; i < MAXN; i++) {
if (!not_prime[i])
for (int j = 2 * i; j < MAXN; j += i) not_prime[j] = true;
}
for (int i = 1; i < MAXN; i++) pre[i] = pre[i - 1] + !not_prime[i];
}
bool check(int mid) {
for (int i = a; i + mid - 1 <= b; i++)
if (pre[i + mid - 1] - pre[i - 1] < k) return false;
return true;
}
void solve() {
init();
cin >> a >> b >> k;
int l = 1, r = b - a + 2; // 注意r的取值范围为l的最大值+1
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << (l == b - a + 2 ? -1 : l);
}
int main() {
solve();
}
248B. Chilly Willy
原题指路:https://codeforces.com/problemset/problem/248/B
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
求一个最小的长度为 n ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n (1≤n≤1e5)的整数使得它能被 2 , 3 , 5 , 7 2,3,5,7 2,3,5,7整除,无解输出 − 1 -1 −1.
思路
显然 n ≤ 2 n\leq 2 n≤2时无解.
对 n ≥ 3 n\geq 3 n≥3的情况,解是 210 210 210的倍数.
① n = 3 n=3 n=3时, a n s = 210 ans=210 ans=210.
② n ≥ 3 n\geq 3 n≥3时,考虑构造一个整数 1 0 ⋯ 0 ⏟ ( n − 4 ) 个 ? ? ? ‾ \overline{1\underbrace{0\cdots 0}_{(n-4)个}???} 1(n−4)个 0⋯0???,其中最后三位用于使得该数模 210 210 210余 0 0 0,大数取模即可.
代码
void solve() {
int n; cin >> n;
if (n <= 2) cout << -1;
else if (n == 3) cout << 210;
else {
int rest = 10; // ans % 210
for (int i = 3; i <= n; i++) rest = (rest * 10) % 210;
rest = 210 - rest; // 加到下一个210的倍数所需的步数
cout << 1;
for (int i = 4; i < n; i++) cout << 0; // 中间的(n-4)位
printf("%03d", rest);
}
}
int main() {
solve();
}
264B. Good Sequences
原题指路:https://codeforces.com/problemset/problem/264/B
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
给定一个长度为 n ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n (1≤n≤1e5)的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an.称 a [ ] a[] a[]的一个子列 x 1 , ⋯ , x k x_1,\cdots,x_k x1,⋯,xk是好的,如果它满足如下两条件:①严格递增,即 x i < x i + 1 ( 1 ≤ i ≤ k − 1 ) x_i<x_{i+1}\ \ (1\leq i\leq k-1) xi<xi+1 (1≤i≤k−1);②任意两相邻元素不互素,即 gcd ( x i , x i + 1 ) > 1 ( 1 ≤ i ≤ k − 1 ) \gcd(x_i,x_{i+1})>1\ \ (1\leq i\leq k-1) gcd(xi,xi+1)>1 (1≤i≤k−1).求最长的好的子列的长度.
思路
d p [ a ] dp[a] dp[a]表示以元素 a a a结尾的满足条件的LIS的长度,最终答案 a n s = max 1 ≤ i ≤ 1 e 5 d p [ i ] \displaystyle ans=\max_{1\leq i\leq 1\mathrm{e}5} dp[i] ans=1≤i≤1e5maxdp[i].
对序列的每个元素 x x x,设它能接到末尾的最长序列长度为 r e s res res.枚举 x x x不超过 x \sqrt{x} x的约数 j j j,则 x x x能接到末尾的状态是 d p [ j ] dp[j] dp[j]和 d p [ x / j ] dp[x/j] dp[x/j],注意 1 1 1是所有数的约数,而只有 j > 1 j>1 j>1时 d p [ j ] dp[j] dp[j]才是合法状态. r e s = max d ∣ x d p [ d ] \displaystyle res=\max_{d\mid x}dp[d] res=d∣xmaxdp[d],将 x x x接到序列末尾,即 r e s + + res++ res++后,对 x x x的约数 d d d,用 r e s res res更新 d p [ d ] dp[d] dp[d]即可.
代码
const int MAXN = 1e5 + 5;
int dp[MAXN]; // dp[a]以元素a结尾的满足条件的LIS的长度
int ans;
void solve() {
int n; cin >> n;
for (int i = 0; i < n; i++) {
int x; cin >> x;
int res = 0;
for (int j = 1; (ll)j * j <= x; j++) {
if (x % j == 0) {
if (j > 1) res = max(res, dp[j]); // 注意j>1时才可转移
res = max(res, dp[x / j]);
}
}
res++;
for (int j = 1; (ll)j * j <= x; j++)
if (x % j == 0) dp[j] = dp[x / j] = res;
ans = max(ans, res);
}
cout << ans;
}
int main() {
solve();
}
271B. Prime Matrix
原题指路:https://codeforces.com/problemset/problem/271/B
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
称一个矩阵是好的,如果至少存在一行或一列使得其中的元素都是素数.给定一个 n × m ( 1 ≤ n , m ≤ 500 ) n\times m\ \ (1\leq n,m\leq 500) n×m (1≤n,m≤500)的整数矩阵,元素范围为 [ 1 , 1 e 5 ] [1,1\mathrm{e}5] [1,1e5].现有操作:选择一个矩阵元素,将其 + 1 +1 +1或 − 1 -1 −1.问将矩阵变为好的矩阵的最小操作次数.
思路
预处理出 1 e 5 1\mathrm{e}5 1e5内的素数,求出一个新的矩阵,其中新的矩阵的元素是原矩阵中对应元素与不小于它的素数的距离,求新矩阵每行和每列的和,取 min \min min即可.
代码
const int MAXN = 1e5 + 5;
int primes[MAXN], cnt;
bool state[MAXN];
void init() {
for (int i = 2; i < MAXN; i++) {
if(!state[i]) primes[cnt++] = i;
for (int j = 0; (ll)primes[j] * i < MAXN; j++) {
state[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
void solve() {
init();
int n, m; cin >> n >> m;
vector<vi> a(n, vi(m));
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++) cin >> a[i][j];
vector<vi> d(n, vi(m)); // 每个元素与它下一个素数的距离
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int nxt = *lower_bound(primes, primes + cnt, a[i][j]);
d[i][j] = nxt - a[i][j];
}
}
int ans = INF;
for (int i = 0; i < n; i++) {
int res = 0;
for (int j = 0; j < m; j++) res += d[i][j];
ans = min(ans, res);
}
for (int j = 0; j < m; j++) {
int res = 0;
for (int i = 0; i < n; i++) res += d[i][j];
ans = min(ans, res);
}
cout << ans;
}
int main() {
solve();
}
284A. Cows and Primitive Roots
原题指路:https://codeforces.com/problemset/problem/284/A
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
对一个素数 p p p,定义模 p p p意义下的原根为 [ 1 , p ) [1,p) [1,p)中的一个整数 x x x,使得 x − 1 , x 2 − 1 , ⋯ , x p − 2 − 1 x-1,x^2-1,\cdots,x^{p-2}-1 x−1,x2−1,⋯,xp−2−1都不能被 p p p整除,但 x p − 1 x^{p-1} xp−1能被 p p p整除.给定一个素数 p ( 2 ≤ p ≤ 2000 ) p\ \ (2\leq p\leq 2000) p (2≤p≤2000),求其原根的个数.
思路I
对 [ 1 , p ) [1,p) [1,p)的每个整数 x x x,枚举其 1 ∼ ( p − 2 ) 1\sim (p-2) 1∼(p−2)次方和 ( p − 1 ) (p-1) (p−1)次方,检查整除性即可,总时间复杂度 O ( p 2 ) O(p^2) O(p2).
代码I
void solve() {
int p; cin >> p;
int ans = 0;
for (int i = 1; i < p; i++) {
bool ok = true;
int cur = 1;
for (int j = 1; j <= p - 2; j++) {
cur = cur * i % p;
if ((cur - 1) % p == 0) {
ok = false;
break;
}
}
if ((cur * i - 1) % p) ok = false;
ans += ok;
}
cout << ans;
}
int main() {
solve();
}
思路II
由原根存在性定理:素数存在原根.
设 g g g是 p p p的一个原根.考察集合 { g , g 2 , ⋯ , g p − 1 } \{g,g^2,\cdots,g^{p-1}\} {g,g2,⋯,gp−1}.若 g i g^i gi是原根,则 gcd ( i , p − 1 ) = 1 \gcd(i,p-1)=1 gcd(i,p−1)=1,则原根个数即 φ ( p − 1 ) \varphi(p-1) φ(p−1),其中 φ ( n ) \varphi(n) φ(n)为Euler函数.
代码II
int phi(int x) {
int res = x;
for (int i = 2; i <= x / i; i++) {
if (x % i == 0) {
res = res / i * (i - 1);
while (x % i == 0) x /= i;
}
}
if (x > 1) res = res / x * (x - 1);
return res;
}
void solve() {
int p; cin >> p;
cout << phi(p - 1);
}
int main() {
solve();
}
337B. Routine Problem
原题指路:https://codeforces.com/problemset/problem/337/B
题意
显示器的宽高比为 a : b a:b a:b,视频的宽高比为 c : d c:d c:d.先将视频放大或缩小使得它占据显示器尽量大的空间,求剩下的空间所占的比例,输出既约分数的形式.
第一行输入四个整数 a , b , c , d ( 1 ≤ a , b , c , d ≤ 1000 ) a,b,c,d\ \ (1\leq a,b,c,d\leq 1000) a,b,c,d (1≤a,b,c,d≤1000).
思路
设显示器的宽、高分别为 W W W、 H H H,则 W H = a b \dfrac{W}{H}=\dfrac{a}{b} HW=ba.设视频的宽、高分别为 w w w、 h h h,则. w h = c d \dfrac{w}{h}=\dfrac{c}{d} hw=dc
①若 a b < c d \dfrac{a}{b}<\dfrac{c}{d} ba<dc,则最优策略是视频与显示器同宽,此时放大倍数 k = W w k=\dfrac{W}{w} k=wW,视频的新高度为 h ′ = h W w = W d c h'=h\dfrac{W}{w}=\dfrac{Wd}{c} h′=hwW=cWd.
a n s = H − h ′ H = W b a − W d c W b a = b c − a d b c ans=\dfrac{H-h'}{H}=\dfrac{\dfrac{Wb}{a}-\dfrac{Wd}{c}}{\dfrac{Wb}{a}}=\dfrac{bc-ad}{bc} ans=HH−h′=aWbaWb−cWd=bcbc−ad.
②若 a b = c d \dfrac{a}{b}=\dfrac{c}{d} ba=dc,则视频恰能占满屏幕, a n s = 0 ans=0 ans=0,注意输出 0 / 1 0/1 0/1.
③若 a b > c d \dfrac{a}{b}>\dfrac{c}{d} ba>dc,最优策略是视频与显示器同高,此时视频的新宽度 w ′ = w H h = w W b a W d c = W b c a d w'=w\dfrac{H}{h}=w\dfrac{\dfrac{Wb}{a}}{\dfrac{Wd}{c}}=\dfrac{Wbc}{ad} w′=whH=wcWdaWb=adWbc.
a n s = W − w ′ W = a d − b c a d ans=\dfrac{W-w'}{W}=\dfrac{ad-bc}{ad} ans=WW−w′=adad−bc.
代码
void solve() {
int a, b, c, d; cin >> a >> b >> c >> d;
if (cmp((double)a / b, (double)c / d) == 0) cout << "0/1";
else if (cmp((double)a / b, (double)c / d) < 0) {
int up = b * c - a * d, down = b * c;
int g = gcd(up, down);
up /= g, down /= g;
cout << up << '/' << down;
}
else {
int up = a * d - b * c, down = a * d;
int g = gcd(up, down);
up /= g, down /= g;
cout << up << '/' << down;
}
}
int main() {
solve();
}
337C. Quiz
原题指路:https://codeforces.com/problemset/problem/337/C
题意
有连续 n n n个问题,玩家每答对一个问题获得 1 1 1分.有一个连续答对题数的计时器,答对一题时计时器 + 1 +1 +1,答错一题时计时器清零.若计时器达到 k k k,先将该问题回答正确的分数加到玩家的分数上后,玩家分数翻倍,同时计时器清零.现已知答对了 m m m个问题,求可获得的最小得分,答案对 1 e 9 + 9 1\mathrm{e}9+9 1e9+9取模.
第一行输入三个整数 n , m , k ( 2 ≤ k ≤ n ≤ 1 e 9 , 0 ≤ m ≤ n ) n,m,k\ \ (2\leq k\leq n\leq 1\mathrm{e}9,0\leq m\leq n) n,m,k (2≤k≤n≤1e9,0≤m≤n).
思路
显然应尽量避免翻倍,若无法避免,尽量在前面翻倍.
为充分利用答错的题数 w r o n g = n − m wrong=n-m wrong=n−m,应在连续 ( k − 1 ) (k-1) (k−1)个答对后放一个答错,则最少翻倍次数为 ⌊ n − k ( n − m ) k ⌋ \left\lfloor\dfrac{n-k(n-m)}{k}\right\rfloor ⌊kn−k(n−m)⌋.
①若该值 ≤ 0 \leq 0 ≤0,则可做到不翻倍, a n s = m ans=m ans=m.
②若该值 > 0 >0 >0,设第 i ( i ≥ 1 ) i\ \ (i\geq 1) i (i≥1)次翻倍后的分数为 f i f_i fi,初始条件 f 0 = 0 f_0=0 f0=0.
易得递推公式 f i = 2 ( f i − 1 + k ) f_i=2(f_{i-1}+k) fi=2(fi−1+k),展开后为等比数列求和,易得通项 f n = ( 2 n + 1 − 2 ) k f_n=(2^{n+1}-2)k fn=(2n+1−2)k.
最后加上剩下的 n % k n\% k n%k道题的分数即可.
代码
void solve() {
int n, m, k; cin >> n >> m >> k;
int wrong = n - m;
if (n <= (ll)k * wrong) {
cout << m;
return;
}
n -= k * wrong;
int x = n / k, y = n % k;
int ans = (qpow(2, x + 1, MOD) - 2 + MOD) % MOD; // 注意qpow(2, x + 1, MOD) - 2可能为负数
ans = (((ll)ans * k % MOD + y) % MOD + (ll)(k - 1) * wrong % MOD) % MOD;
cout << ans;
}
int main() {
solve();
}
343A. Rational Resistance
原题指路:https://codeforces.com/problemset/problem/343/A
题意
给定一个既约分数 a b ( 1 ≤ a , b ≤ 1 e 18 ) \dfrac{a}{b}\ \ (1\leq a,b\leq 1\mathrm{e}18) ba (1≤a,b≤1e18),求最少用多少个阻值为 1 1 1的定值电阻可串联或并联出一个电路使得其总阻值为 a b \dfrac{a}{b} ba.
思路
若能用 k k k个定值电阻构成阻值为 a b \dfrac{a}{b} ba的电路,则用 ( k + 1 ) (k+1) (k+1)个定值电阻能构成阻值为 a + b b \dfrac{a+b}{b} ba+b和 a a + b \dfrac{a}{a+b} a+ba的电路,即加入一个电阻相当于做一次Euclid算法的逆操作,故答案即Euclid算法的执行次数.
代码
void solve() {
ll a, b; cin >> a >> b;
ll ans = 0;
while (a && b) {
if (a > b) {
ans += a / b;
a %= b;
}
else {
ans += b / a;
b %= a;
}
}
cout << ans;
}
int main() {
solve();
}