AtCoder Beginner Contest 330(A~F) abc330题解

AtCoder Beginner Contest 330
A - Counting Passes

统计有几个值 ≥ L \ge L L

时间复杂度 O ( N ) O(N) O(N)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int N, L;
    std::cin >> N >> L;

    int ans = 0;
    for (int i = 0; i < N; i++) {
        int x;
        std::cin >> x;
        ans += x >= L;
    }   

    std::cout << ans << "\n";

    return 0;
}
B - Minimize Abs 1

要使得 ∣ X i − A i ∣ ≤ m i n ∣ Y − A i ∣ |X_i - A_i| \le min|Y - A_i| XiAiminYAi,所以要使得不等式右边最小,Y的取值集合其实只有 L , R , A i {L,R,A_i} L,R,Ai,所以只需要比较一下哪个值使得不等式右边的结果更小就好了。

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int N, L, R;
    std::cin >> N >> L >> R;
    std::vector<int> A(N);
    
    for (int i = 0; i < N; i++) {
        std::cin >> A[i];

        if (A[i] >= L && A[i] <= R) {
            std::cout << A[i] << " ";
            continue;
        }
        if (std::abs(L - A[i]) <= std::abs(R - A[i])) {
            std::cout << L << " ";
        } else {
            std::cout << R << " ";
        }
    }
    return 0;
}
C - Minimize Abs 2

显然就是一个枚举题。

枚举其中一个值,然后根据 D D D来求出另一个值,由于std::sqrt强转之后会向下取整变为int类型,因此每次需要枚举 y , y + 1 y,y+1 y,y+1两个数。每次更新式子的最小值。

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    i64 D;
    std::cin >> D;

    i64 ans = 1E18;
    for (i64 x = 0; ; x++) {
        i64 v = x * x;
        if (v > D) {
            break;
        }

        i64 z = std::sqrt(D - v);
        for (auto y : {z, z + 1}) {
            ans = std::min(ans, std::abs(x * x + y * y - D));
        }
    }

    std::cout << ans << "\n";
    return 0;
}
D - Counting Ls

本题要求形如

oo
o

这样方案的个数,这些o不要求一定相连,那其实我们只需要枚举那个关键节点然后每行每列计算一下除去当前枚举节点还剩下多少字符就好了,那么每次对答案的贡献就是 ( r o w − 1 ) × ( c o l − 1 ) (row-1) \times (col-1) (row1)×(col1),然后将贡献累加起来即可。

时间复杂度 O ( N 2 ) O(N^{2}) O(N2)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int N;
    std::cin >> N;

    std::vector<std::string> S(N);
    for (int i = 0; i < N; i++) {
        std::cin >> S[i];
    }

    std::vector<int> row(N), col(N);
    for (int i = 0; i < N; i++) {
        row[i] = std::count(S[i].begin(), S[i].end(), 'o');
    }

    for (int j = 0; j < N; j++) {
        for (int i = 0; i < N; i++) {
            col[j] += S[i][j] == 'o';
        }
    }

    i64 ans = 0;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (S[i][j] != 'o') {
                continue;
            }
            i64 x = row[i] - 1, y = col[j] - 1;
            ans += x * y;
        }
    }

    std::cout << ans << "\n";
    return 0;
}
E - Mex and Update

题目要求对于每次操作之后求出当前序列的mex。

数据范围 2 E 5 2E5 2E5,也就是说我们需要有 O ( n l o g n ) O(nlogn) O(nlogn)的做法解决上述问题。

注意到mex的性质:当前序列中最小的没有出现的非负整数,因此mex的值至多不会超过 N N N

所以考虑用 s t d : : s e t std::set std::set来维护可能成为mex的数值,同时用 s t d : : m a p std::map std::map来维护某个数值的数量,发生变化时根据情况判断对 s t d : : s e t std::set std::set进行增删。

时间复杂度 O ( ( N + Q ) l o g N ) O((N + Q)logN) O((N+Q)logN)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int N, Q;
    std::cin >> N >> Q;

    std::vector<int> A(N);
    std::set<int> f;
    std::map<int, int> cnt;
    for (int i = 0; i < N + 5; i++) {
        f.emplace(i);
    }
    
    for (int i = 0; i < N; i++) {
        std::cin >> A[i];
        cnt[A[i]] += 1;
        if (cnt[A[i]] == 1) {
            f.extract(A[i]);
        }        
    }

    while (Q--) {
        int i, x;
        std::cin >> i >> x;
        i--;
        if (--cnt[A[i]] == 0) {
            f.emplace(A[i]);
        }

        A[i] = x;
        if (++cnt[A[i]] == 1) {
            f.extract(A[i]);
        }

        std::cout << *f.begin() << "\n";
    }

    return 0;
}
F - Minimize Bounding Square

题目是说要在规定操作步数之内最小化正方形边的值,容易想到二分答案。

至于check函数,我们一定是要将 X Y XY XY坐标分开来考虑的,然后来考虑这样一件事情:

如果在一条直线上有两点 a , b a,b a,b,想要在这条直线上再选择一个点 c c c,使得 d a c + d b c d_{ac}+d_{bc} dac+dbc的距离和最小,那么 c c c点一定在 a b ab ab之间。

再考虑本问题,计算 X X X坐标上的操作数时,先考虑 X X X坐标最外面的两个点,根据上面我们所考虑的,最外面两点到这个矩形的距离为

std::max(0, X[i] - X[j] - x),x为当前正在二分的边长。

Y Y Y坐标上的操作数也同理。

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int N;
    i64 K;
    std::cin >> N >> K;

    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());

    auto check = [&](int x) {
        i64 ans = 0;
        for (int i = 0, j = N - 1; i <= j; i++, j--) {
            ans += 1LL * std::max(0, X[j] - X[i] - x) + std::max(0, Y[j] - Y[i] - x);
        }

        // std::cerr << ans << "\n";
        return ans <= K;
    };
    int lo = -1, hi = int(1E9) + 1;
    while (hi - lo != 1) {
        int m = (lo + hi) / 2;
        if (check(m)) {
            hi = m;
        } else {
            lo = m;
        }
    }

    std::cout << hi << "\n";

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值