ZZJC新生训练赛第二十一场题解
链接:https://ac.nowcoder.com/acm/contest/98790
密码:zzjcacm
难度分类(同一难度下按字典序上升)
- 简单: A, D, F
- 中等: C, E
- 困难: H, B, G
A-解题思路
帅数都是质数的2,3,4次的和,因此预处理出质数之后暴力跑三层循环即可,由于至少是2次的和,所以预处理到根号范围即可,注意要进行一些剪枝提前退出,否则会导致mle
A-代码实现
#include <bits/stdc++.h>
const int N = 1e4 + 10;
std::vector<int> primes;
bool f[N];
void euler(int n) {
for (int i = 2; i <= n; i++) {
if (!f[i]) {
primes.push_back(i);
}
for (int prime : primes) {
if (i * prime > n) {
break;
}
f[i * prime] = true;
if (i % prime == 0) {
break;
}
}
}
}
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
int n;
std::cin >> n;
euler((int)1e4);
std::set<int> st;
for (auto x : primes) {
int x2 = x * x;
if (x2 >= n) {
break;
}
for (auto y : primes) {
int y3 = y * y * y;
if (x2 + y3 >= n) {
break;
}
for (auto z : primes) {
int z4 = z * z * z * z;
int num = x2 + y3 + z4;
if (num > n) {
break;
}
st.insert(num);
}
}
}
std::cout << st.size();
}
D-解题思路
求和第九场的小红走矩阵的区别
D-代码实现
#include <bits/stdc++.h>
const int N = 1e3 + 10;
std::vector<std::vector<int>> g(N, std::vector<int>(N));
int n, m, vis[N][N];
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, -1, 0, 1};
void bfs() {
std::queue<std::tuple<int, int, int>> que;
que.push({0, 0, 0});
vis[0][0] = 1;
while (!que.empty()) {
auto [x, y, z] = que.front();
que.pop();
if (x == n - 1 && y == m - 1) {
std::cout << z;
return;
} else {
for (int i = 0; i < 4; i++) {
int nx = x + dx[i] * g[x][y], ny = y + dy[i] * g[x][y];
if (0 <= nx && nx < n && 0 <= ny && ny < m && !vis[nx][ny]) {
vis[nx][ny] = 1;
que.push({nx, ny, z + 1});
}
}
}
}
std::cout << "No Solution";
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> n >> m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
std::cin >> g[i][j];
}
}
bfs();
}
F-解题思路
3e5暴力显然不可取,但是发现曼哈顿距离的x和y可以分开算,对xy分别排序求前缀和即可获得答案
F-代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n;
std::cin >> n;
std::vector<int> x(n), y(n);
for (int i = 0; i < n; i++) {
std::cin >> x[i] >> y[i];
}
std::sort(x.begin(), x.end());
std::sort(y.begin(), y.end());
std::vector<i64> prex(n + 1), prey(n + 1);
for (int i = 1; i <= n; i++) {
prex[i] = prex[i - 1] + x[i - 1];
prey[i] = prey[i - 1] + y[i - 1];
}
i64 ans = 0;
for (int i = 1; i <= n; i++) {
ans += (x[i - 1] + y[i - 1]) * (i - 1) - prex[i - 1] - prey[i - 1];
}
std::cout << ans;
}
C-解题思路
可以运用前后缀数组来统计当前位置有多少的两侧0和1,对于每个0的位置进行判断,pre表示有多少个1需要后移,suff表示需要有多少个0前移动,因此统计pre与0位置(或者suff与1位置)时前后需要换的数字有多少即可。
C-代码实现
#include <bits/stdc++.h>
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
std::string s;
std::cin >> s;
int n = s.size();
s = " " + s;
std::vector<int> pre(n + 1), suff(n + 2);
for (int i = 1; i <= n; i++) {
pre[i] += pre[i - 1] + (s[i] == '1');
}
for (int i = n; i >= 0; i--) {
suff[i] += suff[i + 1] + (s[i] == '0');
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (s[i] == '0' && pre[i]) {
ans = std::max(ans, pre[i] + suff[i] - 1);
}
}
std::cout << ans;
}
E-解题思路
xjb乱搞一下就发现答案是\(m * n * (m * n - (m + n - 1)) - 4 * n * (n - 1) * (n - 2) / 3 - 2 * (m - n + 1) * n * (n - 1)\)(要求\(n \leq m\)),观察数据范围后请选择你的英雄 ——\(C++高精度\) or \(Python魅力时刻\)
E-代码实现
n, m = map(int, input().split())
if n > m:
n, m = m, n
print(m * n * (m * n - (m + n - 1)) - 4 * n * (n - 1) * (n - 2) // 3 - 2 * (m - n + 1) * n * (n - 1))
H-解题思路
假设有两个朋友 \(A(x_1, y_1)\) 和 \(B(x_2, y_2)\),我们需要找到与这两个朋友的距离相等的点的集合。这些点形成一条直线,也就是 分界线。
假设 fresh_boy 的坐标为 \((X, Y)\),那么:
fresh_boy到 \(A(x_1, y_1)\) 的距离为:\(\text{distance}(A) = \sqrt{(X - x_1)^2 + (Y - y_1)^2}\)fresh_boy到 \(B(x_2, y_2)\) 的距离为:\(\text{distance}(B) = \sqrt{(X - x_2)^2 + (Y - y_2)^2}\)
为了求得 fresh_boy 到 \(A\) 和 $ B $ 的距离相等的所有点,我们将两者的平方距离进行比较:
\(\sqrt{(X - x_1)^2 + (Y - y_1)^2} = \sqrt{(X - x_2)^2 + (Y - y_2)^2}\)
去掉平方根,得到:
\((X - x_1)^2 + (Y - y_1)^2 = (X - x_2)^2 + (Y - y_2)^2\)
将两边的平方展开并整理,得到:
\((X^2 - 2x_1X + x_1^2 + Y^2 - 2y_1Y + y_1^2) = (X^2 - 2x_2X + x_2^2 + Y^2 - 2y_2Y + y_2^2)\)
移项并简化:
\(-2x_1X - 2y_1Y + x_1^2 + y_1^2 = -2x_2X - 2y_2Y + x_2^2 + y_2^2\)
进一步整理,得到:
\(2(x_2 - x_1)X + 2(y_2 - y_1)Y = x_2^2 + y_2^2 - x_1^2 - y_1^2\)
将上式转换为标准的直线方程形式 $ A \cdot X + B \cdot Y + C = 0 $,其中:
\(A = 2(x_2 - x_1)\)
\(B = 2(y_2 - y_1)\)
\(C = x_2^2 + y_2^2 - x_1^2 - y_1^2\)
对ABC整理去重后即可求得答案。
H-代码实现
#include <bits/stdc++.h>
std::array<int, 3> calc(int A, int B, int C) {
int g = std::__gcd(std::__gcd(A, B), C);
A /= g;
B /= g;
C /= g;
if (A < 0 || (A == 0 && B < 0)) {
A = -A;
B = -B;
C = -C;
}
return {A, B, C};
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int t;
std::cin >> t;
while (t--) {
int n;
std::cin >> n;
std::vector<std::array<int, 2>> pos(n);
for (int i = 0; i < n; i++) {
std::cin >> pos[i][0];
}
for (int i = 0; i < n; i++) {
std::cin >> pos[i][1];
}
std::set<std::array<int, 3>> st;
for (int i = 0; i < n; i++) {
auto [x1, y1] = pos[i];
for (int j = i + 1; j < n; j++) {
auto [x2, y2] = pos[j];
int A = 2 * (x2 - x1);
int B = 2 * (y2 - y1);
int C = x2 * x2 - x1 * x1 + y2 * y2 - y1 * y1;
st.insert(calc(A, B, C));
}
}
std::cout << st.size() << "\n";
}
}
B-解题思路
观察数据范围发现n很小,但是暴力搜索全排列是17!过高,而数据范围也通用符合状压dp的范畴,状压的二进制表示同样适用于这题分配,因此可以使用状压dp来完成本题。
B-代码实现
#include <bits/stdc++.h>
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n;
std::cin >> n;
std::vector<std::vector<int>> g(n, std::vector<int>(n));
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
std::cin >> g[i][j];
}
}
std::vector<int> dp(1 << n);
for (int i = 0; i < (1 << n); i++) {
for (int j = 0; j < n; j++) {
int cnt = __builtin_popcount(i); // 数二进制下有多少个1
if (!(i & (1 << j))) { // 如果状态 i 没有选择任务 j
int s = i | (1 << j);
dp[s] = std::max(dp[s], dp[i] + g[cnt][j]);
}
}
}
std::cout << dp[(1 << n) - 1];
}
G-解题思路
数位DP模板题
G-代码实现
#include <bits/stdc++.h>
using i64 = long long;
i64 dfs(std::string s, int k, int pos, int odd, int even, bool limit, std::map<std::array<int, 4>, i64>& dp) { //注意要引用,不然每次新开一个会导致tle
if (pos == s.length()) {
return (even - odd == k);
}
std::array<int, 4> state = {pos, odd, even, limit};
if (dp.find(state) != dp.end()) {
return dp[state];
}
int maxn = limit ? s[pos] - '0' : 9;
i64 res = 0;
for (int i = 0; i <= maxn; i++) {
if ((s.length() - pos) % 2 == 0) {
res += dfs(s, k, pos + 1, odd, even + i, limit && (i == maxn), dp);
} else {
res += dfs(s, k, pos + 1, odd + i, even, limit && (i == maxn), dp);
}
}
dp[state] = res;
return res;
}
i64 calc(std::string s, int k) {
std::map<std::array<int, 4>, i64> dp;
return dfs(s, k, 0, 0, 0, true, dp);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
i64 l, r, k;
std::cin >> l >> r >> k;
std::cout << calc(std::to_string(r), k) - calc(std::to_string(l - 1), k);
}

浙公网安备 33010602011771号