2022 OI 集训营普及组第一场题解
T1 学习除法
本题等价于判断质数,若原数字为质数,则输出 0,否则输出 1
因为对于每
#include <iostream> using namespace std; int main(){ long long n; cin >> n; for(int i = 2; i <= n / i; i++){ if(n % i == 0){ cout << 1 << endl; return 0; } } cout << 0 << endl; }
一个大于等于 2 的数字,一定可以找到另一个数字可以将它除成质数(素因子分解定理)
T2 拆分
考虑有多少个集合的 mex 可以至少为 1——这些集合必须有一个 0
所以假设 0 有
个,那么就说明可以拆出来
个集合 mex 至少为 1,此时答案增加
(这一步是考虑这些集合对答案的贡献)。
再考虑有多少个集合的 mex 可以至少为 2——这些集合必须有一个 1,并且 mex 至少为 2 的集合的数量一定小于等于 mex 至少为 1 的集合,所以涉及到一个求 min 的操作。
所以假设 0 有
再考虑有多少个集合的 mex 可以至少为 2——这些集合必须有一个 1,并且 mex 至少为 2 的集合的数量一定小于等于 mex 至少为 1 的集合,所以涉及到一个求 min 的操作。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; int a, cnt[maxn]; int main(){ int n; cin >> n; memset(cnt, 0, sizeof cnt); for(int i = 1; i <= n; i++){ cin >> a; cnt[a]++; } int now = cnt[0], ans = now; for(int i = 1; i <= 1000; i++){ now = min(now, cnt[i]); ans += now; } cout << ans << endl; }
T3 部落
考察 nlogn 求最长上升子序列,求得最长上升子序列之后,我们还要去考虑最上面那一个点是否需要修改高度,进行分类讨论即可。
#include <iostream> using namespace std; const int maxn = 1e5 + 5; int a[maxn], tp[maxn]; int main(){ int n, pos, p = 1, ans = 0; cin >> n >> pos; for(int i = 1; i <= n; i++){ cin >> a[i]; } tp[1] = a[1]; for(int i = 2; i < pos; i++){ if(a[i] >= tp[p]) tp[++p] = a[i]; else{ int l = 1, r = p, mid = p >> 1; while(l != r){ if(a[i] < tp[mid]) r = mid; else l = mid + 1; mid = (l+r) >> 1; } tp[l] = a[i]; } } if(a[pos] >= tp[p]){ p++; // 不必修改最上面的点的高度 }else{ a[pos] = 1e9 + 1; // 修改最上面的点的高度,将其变为无穷大 } ans = pos - p; p = 1; tp[1] = a[pos]; for(int i = pos + 1; i <= n; i++){ if(a[i] <= tp[p]) tp[++p] = a[i]; else{ int l = 1, r = p, mid = p >> 1; while(l != r){ if(a[i] > tp[mid]) r = mid; else l = mid + 1; mid = (l + r) >> 1; } tp[l] = a[i]; } } ans += (n - pos + 1) - p; cout << ans << endl; }
T4 传递
每次转移有 4 种情况,要么 A 青蛙跳跃,要么 B 青蛙跳跃,要么传递跳跃器。
如果这样写
每次跳跃都是跳跃到下一个石子就行,不必跨越多个石子跳跃。因为本题考虑的是最少传递次数,和跳跃次数无关。
#include <bits/stdc++.h> using namespace std; const int maxn = 1005; int a[maxn], b[maxn], dp[maxn][maxn][2]; int main(){ int n, m, k, q; cin >> n >> m >> k >> q; for(int i = 1; i <= n; i++){ cin >> a[i]; } for(int i = 1; i <= n; i++){ cin >> b[i]; } a[n+1] = a[n]; b[n+1] = b[n]; memset(dp, 0x3f, sizeof dp); dp[0][0][0] = 0; dp[0][0][1] = 1; for(int step = 0; step <= 2 * n; step++){ for(int j = 0; j <= min(n, step); j++){ int i = step - j; if(i > n) continue; for(int k = 0, r = 0; r <= 3; k++, r++){ // 枚举第三维时,在原地转移 4 次,这样 0 1 都被互相求 min k = r % 2; if(dp[i][j][k] >= 1e9) continue; if(abs(a[i] - b[j]) <= q) dp[i][j][k^1] = min(dp[i][j][k^1], dp[i][j][k] + 1); if(k == 0){ dp[i+1][j][k] = min(dp[i+1][j][k], dp[i][j][k]); if(b[j+1] - b[j] <= m){ dp[i][j+1][k] = min(dp[i][j+1][k], dp[i][j][k]); } }else{ dp[i][j+1][k] = min(dp[i][j+1][k], dp[i][j][k]); if(a[i+1] - a[i] <= m){ dp[i+1][j][k] = min(dp[i+1][j][k], dp[i][j][k]); } } } } } cout << min(dp[n][n][0], dp[n][n][1])<< endl; }