Codeforces Round #641 (Div. 2)
A. Orac and Factors
题目描述
定义 \(f_i\) 为 \(i\) 的最小非 \(1\) 因数乘以 \(i\)
给定 \(n,k\),求进行 \(k\) 次 \(n = f(n)\) 后的 \(n\) 的值
思路
模拟几个数字容易发现 进行过一次 \(n = f(n)\) 之后, \(n\) 一定是个偶数
偶数的最小非 \(1\) 因数一定是 \(2\)
点击查看代码
/********************
Author: Nanfeng1997
Contest: Codeforces Round #641 (Div. 2)
URL: https://codeforces.com/contest/{getProblemIndexes(problemCurrentPageList[i][0])[0]}/problem/A
When: 2022-03-17 22:38:20
Memory: 256MB
Time: 2000ms
********************/
#include <bits/stdc++.h>
#define _ 0
using namespace std;
using LL = long long;
void solve() {
LL n, k; cin >> n >> k;
LL i = 0;
for(i = 2; i <= n / i; i ++ ) {
if(n % i == 0) {
break;
}
}
if(n % i) i = n;
if(k == 1) {
cout << n + i << "\n";
} else {
n += i;
k --;
cout << n + k * 2 << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1; cin >> T;
while(T --) solve();
return ~~(0 ^ _ ^ 0);
}
B. Orac and Models
题目描述
略...
思路
进行 \(n \sqrt(n)\) 的 \(DP\) 即可
点击查看代码
/********************
Author: Nanfeng1997
Contest: Codeforces Round #641 (Div. 2)
URL: https://codeforces.com/contest/{getProblemIndexes(problemCurrentPageList[i][0])[0]}/problem/B
When: 2022-03-17 22:38:21
Memory: 256MB
Time: 3000ms
********************/
#include <bits/stdc++.h>
#define _ 0
using namespace std;
using LL = long long;
const int N = 1e5 + 10;
struct node {
int val, id;
bool operator < (const node &T) const {
if(val != T.val)
return val < T.val;
return id < T.id;
}
}a[N]; //结构体没什么用,懒得删了~~~
int f[N];
void solve() {
int n; cin >> n;
for(int i = 1; i <= n; i ++ ) {
cin >> a[i].val;
a[i].id = i;
f[i] = 1;
}
int ans = 1;
for(int i = 1; i <= n; i ++ ) {
for(int j = 1; j <= i / j; j ++ ) {
if(i % j == 0) {
if(a[i].val > a[j].val)
f[i] = max(f[i], f[j] + 1);
if(j != 1 && a[i].val > a[i / j].val)
f[i] = max(f[i], f[i / j] + 1);
}
}
}
for(int i = 1; i <= n; i ++ ) {
ans = max(ans, f[i]);
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1; cin >> T;
while(T --) solve();
return ~~(0 ^ _ ^ 0);
}
C. Orac and LCM
题目描述
- 给定长度为 \(n\) 的序列,\(a_1, a_2, a_3,...,a_n\)
- 设可重集 \(S ={lcm(a_i, a_j)|1 \le i < \le n}\)。
- 设一个集合的最大公约数为最大正整数 \(d\),满足集合内任意一个元素 \(x\) 均有 \(d|x\)。
- 请你求出 \(\gcd(S)\)
- \(1 \le n \le 10^5,1 \le a_i \le 2 \times 10^5\)。
思路
首先: \(lcm(a, b) = \frac{a \times b}{gcd(a, b)}\)
所以原式可以化为
把 \(gcd(a_i, a_j)\) 提出可得
\(gcd(a_1, a_2,...,a_n)\)可以线性求出,问题就转化成了如何快速求 \(gcd \left \{ a_i \times a_j | i < j \right \}\)
设我们每次枚举到 第 \(i\) 个数字 \(a_i\) 为 \(x\),那么可以将 \(gcd \left \{ x \times a_j | i < j \right \}\) 中的 \(x\) 提出,就可以得到 \(x \times gcd(a_{i + 1}, a_{i+1},..., a_n)\)
可以预处理出后缀 \(gcd\) ,枚举 \(a_i\) 即可计算答案
复杂度 \(O(n \log a_{max})\)
源自
对于上述解法的证明:
如何证明这个等式?
对于每个质数 \(p\), \(gcd \left \{ a_ia_j | i < j \right \}\) 相当于求出了其指数的最小值和次小值之和,\(gcd \left \{ gcd(a_ia,_j) | i < j \right \}\) 则相当于求出了其指数的最小值,两者相减便是答案。
上述做法代码
点击查看代码
/********************
Author: Nanfeng1997
Contest: Codeforces Round #641 (Div. 2)
URL: https://codeforces.com/contest/{getProblemIndexes(problemCurrentPageList[i][0])[0]}/problem/C
When: 2022-03-17 22:38:22
Memory: 256MB
Time: 3000ms
********************/
#include <bits/stdc++.h>
#define _ 0
using namespace std;
using LL = long long;
const int N = 1e5 + 10, M = 2e5 + 10;
int a[N], b[N];
/*
lcm是求两个数字的质因子的最大值
gcd是求两个数字的质因子的最小值
最大值的最小值 就是次小值.jpg
怎么快速的求呢?
我们可以先求一个 最小值和次小值 的和 就是 gcd(gcd(a_i * a_j))
然后求一个最小值 gcd(gcd(a_i, a_j))
*/
void solve() {
int n; cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
for(int i = n; i >= 1; i -- ) b[i] = __gcd(b[i + 1], a[i]);
LL ans = 0;
for(int i = 1; i <= n; i ++ ) {
ans = __gcd(ans, 1LL * a[i] * b[i + 1]);
}
printf("%lld", ans / b[1]);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1; // cin >> T;
while(T --) solve();
return ~~(0 ^ _ ^ 0);
}
另一种做法
点击查看代码
/********************
Author: Nanfeng1997
Contest: Codeforces Round #641 (Div. 2)
URL: https://codeforces.com/contest/{getProblemIndexes(problemCurrentPageList[i][0])[0]}/problem/C
When: 2022-03-17 22:38:22
Memory: 256MB
Time: 3000ms
********************/
#include <bits/stdc++.h>
#define _ 0
using namespace std;
using LL = long long;
const int N = 1e5 + 10, M = 2e5 + 10;
int a[N], f[N];
int pre[N], suf[N];
void solve() {
/*
如果p^k | ans, 那么必定有多于 n-1 个数字可以满足 p^k | a_i
如果是 少于 n - 1 个,那么必定有两个数字 a_i, a_j,满足 p^k not| lcm(a_i, a_j)
因此不成立
因此维护前缀gcd和后缀gcd即可
*/
int n; cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
for(int i = 1; i <= n; i ++ ) pre[i] = __gcd(pre[i - 1], a[i]);
for(int i = n; i >= 1; i -- ) suf[i] = __gcd(suf[i + 1], a[i]);
int g = 0;
for(int i = 1; i <= n; i ++ ) {
f[i] = __gcd(pre[i - 1], suf[i + 1]);
g = __gcd(g, f[i]);
}
LL ret = 1;
for(int i = 1; i <= n; i ++ ) {
ret = ret * f[i] / g;
}
cout << ret * g;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1; // cin >> T;
while(T --) solve();
return ~~(0 ^ _ ^ 0);
}
D. Orac and Medians
询问数组中的数字能否通过若干次将任意区间内的数字都赋值为其中位数这个操作,使得整个序列的数字都变成 \(k\)
思路
如果数组中不存在 \(k\), 那么一定是不可能的,下面我们考虑数组中存在 \(k\) 的情况
如果数组的长度为 \(1\),那么一定可以,面我们考虑数组中存在 \(k\) 且长度不为 \(1\) 的情况
假如存在 \(a_i \ge k\)并且$ a_{i+1} \ge k$ 那么一定可以从这两个位置往外扩散使得最终的结果均为\(k\)
假如存在 \(a_i \ge k\)并且 $ a_{i+2} \ge k$ 那么一定可以从这两个数字往外扩散将其都变为 大于等于\(k\) 的数字,直到遇到一个 \(k\),使得所有的数字都变为 \(k\)
点击查看代码
/********************
Author: Nanfeng1997
Contest: Codeforces Round #641 (Div. 2)
URL: https://codeforces.com/contest/{getProblemIndexes(problemCurrentPageList[i][0])[0]}/problem/D
When: 2022-03-17 22:38:23
Memory: 256MB
Time: 2000ms
********************/
#include <bits/stdc++.h>
#define _ 0
using namespace std;
using LL = long long;
bool solve() {
int n, k; cin >> n >> k;
vector<int> a(n); int cnt = 0;
for(int i = 0; i < n; i ++ ) {
cin >> a[i];
if(a[i] == k) cnt ++;
}
if(!cnt) return false;
if(n == 1) return true;
for(int i = 0; i + 1 < n; i ++ ) if(a[i] >= k && a[i + 1] >= k) {
return true;
}
for(int i = 0; i + 2 < n; i ++ ) if(a[i] >= k && a[i + 2] >= k) {
return true;
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1; cin >> T;
while(T --) cout << (solve() ? "yes" : "no") << "\n";
return ~~(0 ^ _ ^ 0);
}