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);
}
posted @ 2024-12-09 20:37  udiandianis  阅读(78)  评论(0)    收藏  举报