A. Olympiad Date
点击查看代码
void solve() {int n;std::cin >> n;std::vector<int> a(10);a[0] = 3;a[1] = 1;a[3] = 1;a[2] = 2;a[5] = 1;std::vector<int> b(n);for (int i = 0; i < n; ++ i) {std::cin >> b[i];}std::vector<int> cnt(10);for (int i = 0; i < n; ++ i) {cnt[b[i]] = std::min(cnt[b[i]] + 1, a[b[i]]);if (cnt == a) {std::cout << i + 1 << "\n";return;}}std::cout << 0 << "\n";
}
B. Team Training
题意:把数组划分为若干个集合,每个集合的价值为集合大小乘集合最小值。求最多划分出来几个价值大于等于\(x\)的集合。
从大到小枚举,因为优先用大的数可以使当前集合大小最小,也就是用最少的数。
点击查看代码
void solve() {int n, x;std::cin >> n >> x;std::vector<int> a(n);for (int i = 0; i < n; ++ i) {std::cin >> a[i];}std::ranges::sort(a);int ans = 0;for (int i = n - 1, cnt = 0; i >= 0; -- i) {++ cnt;if ((i64)cnt * a[i] >= x) {++ ans;cnt = 0;}}std::cout << ans << "\n";
}
C. Combination Lock
题意:构造一个长度为\(n\)的排列,使得在接下来\(n-1\)次右移中都有一个位置使得\(p_i = i\)。
设\(i\)需要右移\(a_i\)次可以到\(i\),那么实际上\(a\)就是\(0\)到\(n-1\)的一个排列。发现\(n\)为奇数的情况,从大到小排可以满足这个条件。
而偶数不可以(猜的)
点击查看代码
void solve() {int n;std::cin >> n;if (n % 2 == 0) {std::cout << -1 << "\n";return;}std::vector<int> ans(n);std::ranges::iota(ans, 1);std::reverse(ans.begin(), ans.end());for (int i = 0; i < n; ++ i) {std::cout << ans[i] << " \n"[i == n - 1];}
}
D. Place of the Olympiad
题意:给你一个\(n\times m\)的矩阵,你要放\(k\)个元素进去,使得每行最大连续子段的最大值最小。
二分。
如果一行最多放\(mid\)个元素,那么这一行总共可以放\(\lfloor \frac{m}{mid + 1} \rfloor + m \% (mid + 1)\)个元素。
点击查看代码
void solve() {i64 n, m, k;std::cin >> n >> m >> k;auto check = [&](i64 len) -> i64 {i64 cnt = m / (len + 1) * len + m % (len + 1);return n * cnt;};i64 l = 1, r = m;while (l < r) {i64 mid = l + r >> 1ll;if (check(mid) >= k) {r = mid;} else {l = mid + 1;}}std::cout << l << "\n";
}
E. Interesting Ratio
题意:求\([1, n]\)中有多少对数\(a, b\)满足\(a < b, \frac{lcm(a, b)}{gcd(a, b)}\)是质数。
\(lcm(a, b) = \frac{ab}{gcd(a, b)}, \frac{lcm(a, b)}{gcd(a, b)} = \frac{a}{gcd(a, b)} \times \frac{b}{gcd(a, b)}\),显然只有在\(a\)和\(b\)中有一个数是\(gcd\)的时候且另一个数除\(gcd\)是质数的情况才合法。
那么我们就是要求每个\(i\)在\([i + 1, n]\)内的质数倍数的个数。
预处理筛质数,然后二分求即可。
点击查看代码
std::vector<int> minp, primes;void sieve(int n) {minp.assign(n + 1, 0);primes.clear();for (int i = 2; i <= n; ++ i) {if (minp[i] == 0) {minp[i] = i;primes.push_back(i);}for (auto p : primes) {if (i * p > n) {break;}minp[i * p] = p;if (p == minp[i]) {break;}}}
}void solve() {int n;std::cin >> n;i64 ans = 0;for (int i = 1; i <= n; ++ i) {ans += std::ranges::upper_bound(primes, n / i) - primes.begin();}std::cout << ans << "\n";
}
F. Igor and Mountain
题意:给你一个\(n\times m\)的矩阵,有些地方可以走有些地方不能走。每一行你最多走两个点,当前行只能到下一行,且每次走的两个的欧拉距离不能超过\(d\)。求从最低层到最高层的走法方案数。
\(f[0/1][i][j]\)表示在第\(i\)行走了\(1\)个或者\(2\)个点现在在第\(j\)列的方案数。
\(sum[0/1][i][j]\)表示\(f[0/1][i]\)的前缀和。
\(L[j], R[j]\)表示跨一行的情况下可以\(j\)可以走到点的左右边界。
那么\(f[0][i][j] = sum[0][i - 1][R[j]] - sum[0][i - 1][L[j] - 1] + sum[1][i - 1][R[j]] - sum[1][i - 1][L[j] - 1]\),意味从上一行到当前点。
然后考虑当前行走两个点的情况,那么\(f[1][i][j] = sum[0][i][j + k] - sum[0][i][j - k - 1] - f[0][i][j]\)。
代码省略取模类。
点击查看代码
void solve() {int n, m, k;std::cin >> n >> m >> k;std::vector<std::string> s(n + 1);for (int i = 1; i <= n; ++ i) {std::cin >> s[i];}std::reverse(s.begin() + 1, s.end());auto get_dist = [&](int x1, int y1, int x2, int y2) -> int {int dx = x1 - x2, dy = y1 - y2;return dx * dx + dy * dy;};std::vector f(2, std::vector(n + 1, std::vector<Z>(m + 1)));std::vector sum(2, std::vector(n + 1, std::vector<Z>(m + 1)));for (int j = 1; j <= m; ++ j) {f[0][1][j] = s[1][j - 1] == 'X';sum[0][1][j] = sum[0][1][j - 1] + f[0][1][j];}for (int j = 1; j <= m; ++ j) {if (s[1][j - 1] == 'X') {int l = std::max(1, j - k), r = std::min(m, j + k);f[1][1][j] = sum[0][1][r] - sum[0][1][l - 1] - f[0][1][j];}sum[1][1][j] = sum[1][1][j - 1] + f[1][1][j];}std::vector<int> L(m + 1), R(m + 1);for (int i = 1, j = 1; i <= m; ++ i) {while (get_dist(1, i, 0, j) > k * k) {++ j;}L[i] = j;}for (int i = m, j = m; i >= 1; -- i) {while (get_dist(1, i, 0, j) > k * k) {-- j;}R[i] = j;}for (int i = 2; i <= n; ++ i) {for (int j = 1; j <= m; ++ j) {if (s[i][j - 1] == 'X') {f[0][i][j] += sum[0][i - 1][R[j]] - sum[0][i - 1][L[j] - 1];f[0][i][j] += sum[1][i - 1][R[j]] - sum[1][i - 1][L[j] - 1];}sum[0][i][j] = sum[0][i][j - 1] + f[0][i][j];}for (int j = 1; j <= m; ++ j) {if (s[i][j - 1] == 'X') {int l = std::max(1, j - k), r = std::min(m, j + k);f[1][i][j] = sum[0][i][r] - sum[0][i][l - 1] - f[0][i][j];}sum[1][i][j] = sum[1][i][j - 1] + f[1][i][j];}}Z ans = sum[0][n][m] + sum[1][n][m];std::cout << ans << "\n";
}