AtCoder Beginner Contest 343(小白来了)

youhualiuh / 2024-03-03 / 原文

A - Wrong Answer

思路:

给你两个数 (A, B 0 ~ 9)输出非A+B(0 ~ 9)

解法:

许多 (A + B) ^ 1等等

Code:

#include<iostream>

using namespace std;

int main() {
    int A, B; cin >> A >> B;
    cout << !(A + B);
    return 0;
}

  

B - Adjacency Matrix

思路:(输出邻接表)

如果是1代表连通 输出索引即可

Code:

#include<iostream>

using namespace std;

int main() {
    int N; cin >> N;
    for (int T = 1; T <= N; ++T) {
        for (int i = 1, x; i <= N; ++i) {
            cin >> x;
            if (x) cout << i << ' ';
        }
        cout << '\n';
    }
    return 0;
}

  

  

C - 343

思路:

回文立方体输出<=N的这个最大回文立方体 

Code:最优秀的代码来了

#include<iostream>
#include<string>
#include<cmath>

using namespace std;
using i64 = long long;

bool isPalindrome(i64 n) {
    string str = to_string(n);
    i64 i = 0, j = str.length() - 1;
    while (i < j) {
        if (str[i++] != str[j--]) {
            return false;
        }
    }
    return true;
}

int main() {
    i64 N;  cin >> N;
    for (i64 root = cbrtl(N); root >= 1; --root) {
        i64 cube = root * root * root;
        if (isPalindrome(cube)) {
            cout << cube; return 0;
        }
    }
    return 0;
}

  

Code:(暴力 -》不推荐)

#include<iostream>

using namespace std;
using i64 = long long;

int main() {
    i64 N, ans = 1; cin >> N;
    for (i64 i = 2; i * i * i <= N; i++) {
        i64 k = i * i * i, j = 0;
        while (k) {
            j = j * 10 + k % 10, k /= 10;
        }
        if (j == i * i * i) {
            ans = j;
        }
    }
    return cout << ans << '\n', 0;
}

  

Code:(字符串)

#include<iostream>
#include<string>
#include <algorithm>

using namespace std;
using i64 = long long;

int main() {
    i64 N, ans = 1; cin >> N;
    for (i64 i = 2; i * i * i <= N; i++) {
        string s = to_string(i * i * i);
        string ss = s;
        reverse(s.begin(), s.end());
        if (ss == s) ans = i * i * i;
    }
    return cout << ans, 0;
}

  

 D - Diversity of Scores

思路:

每个人初始值为0, 计算每一次加分10/20/30时的每一次不同数字的个数

解法:

初始化0为N,且刚开始肯定是1,因为都是0,所有我们可以用map<values值, 人数> mp来储存这个值,对于每一次相加B,mp[W[A]]就要减去一个人数到了新的地方,以此类推即可写出此题

Code:

#include<iostream>
#include<map>

using namespace std;
using i64 = long long;
const int maxn = 2e5 + 5;

map<i64, i64> mp; 
i64 W[maxn], ans = 1, N, T, A, B;

int main() {
    cin >> N >> T;
    mp[0] = N;
    for (i64 i = 1; i <= T; i++) {
        cin >> A >> B;
        mp[W[A]] --;
        if (!mp[W[A]]) -- ans;
        W[A] += B;
        if (!mp[W[A]]) ++ ans;
        ++mp[W[A]];
        cout << ans << '\n'; 
    }
    return 0;
}

  

E - 7x7x7

思路:

我想在空间中放置3个7*7*7的立方体,且顶点坐标是整数,且平行于轴有就输出坐标,没有输出-1

Code:

#include<bits/stdc++.h>

using namespace std;

// 定义三维点结构体
struct P {
    int x, y, z;
    P(int x=0, int y=0, int z=0): x(x), y(y), z(z) {}
};

// 计算三个点所围成的长方体的体积
int f(P a, P b, P c) {
    P l, r;
    // 左下角顶点的坐标
    l.x = min({a.x, b.x, c.x}); l.y = min({a.y, b.y, c.y}); l.z = min({a.z, b.z, c.z});
    // 右上角顶点的坐标
    r.x = max({a.x, b.x, c.x}); r.y = max({a.y, b.y, c.y}); r.z = max({a.z, b.z, c.z});
    // 计算体积并返回
    int ans = 1;
    ans *= max(l.x + 7 - r.x, 0); ans *= max(l.y + 7 - r.y, 0); ans *= max(l.z + 7 - r.z, 0);
    return ans;
}

int main() {
    // 输入三个给定的体积
    int v1, v2, v3; cin >> v1 >> v2 >> v3;
    // 如果给定的体积不符合条件,输出"No"并结束程序
    if (v1 != 7*7*7*3 - v2*2 - v3*3) {
        cout << "No" << '\n';
        return 0;
    }
    // 生成候选点集合
    vector<P> vec;
    for (int x = -7; x <= 7; x++) {
        for (int y = -7; y <= 7; y++) {
            for (int z = -7; z <= 7; z++) {
                vec.emplace_back(x,y,z);
            }
        }
    }
    P p0;
    // 遍历所有可能的组合
    for (P p1 : vec) {
        for (P p2 : vec) {
            // 如果第二个体积不符合要求,跳过当前组合
            if (f(p0, p1, p2) != v3) continue;
            // 计算第一个体积
            int now = f(p0, p1, p1);
            now += f(p1, p2, p2);
            now += f(p2, p0, p0);
            now -= v3*3;
            // 如果第一个体积不符合要求,跳过当前组合
            if (now != v2) continue;
            // 输出结果并结束程序
            cout << "Yes" << '\n';
            cout << p0.x << " " << p0.y << " " << p0.z << " ";
            cout << p1.x << " " << p1.y << " " << p1.z << " ";
            cout << p2.x << " " << p2.y << " " << p2.z << '\n';
            return 0;
        }
    }
    // 若未找到满足条件的组合,则输出"No"
    cout << "No" << '\n';
    return 0;
}

  

F - Second Largest Query

思路:

1 p x 将A[p] = x

2 l r 将 l, r的区间里求出第二大的个数

线段树上,每个节点维护这个区间对应的最大值、次大值、以及他们各自的出现次数

解法:

线段树板子加上求第二大的数即可过

Code:

 

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;
template<class Info,
    class Merge = std::plus<Info>>
struct SegmentTree {
    const int n; // 存储节点数量
    const Merge merge; // 存储合并操作的函数对象
    vector<Info> info; // 存储每个节点的信息数组

    // 构造函数,传入节点数量,初始化合并操作和信息数组
    SegmentTree(int n) : n(n), merge(Merge()), info(4 << std::__lg(n)) {}

    // 构造函数,传入初始数组,调用上述构造函数进行初始化
    SegmentTree(vector<Info> init) : SegmentTree(init.size()) {
        // 递归构建 Segment Tree
        function<void(int, int, int)> build = [&](int p, int l, int r) {
            // 当节点范围为1时,直接赋值为对应的初始值
            if (r - l == 1) {
                info[p] = init[l];
                return;
            }
            // 否则递归构建左右子树,并合并信息
            int m = (l + r) / 2;
            build(2 * p, l, m);
            build(2 * p + 1, m, r);
            pull(p);
        };
        // 从根节点开始递归构建
        build(1, 0, n);
    }

    // 更新节点信息函数,根据合并操作更新节点信息
    void pull(int p) {
        info[p] = merge(info[2 * p], info[2 * p + 1]);
    }

    // 更新函数,修改指定位置的值为给定的新值
    void modify(int p, int l, int r, int x, const Info &v) {
        if (r - l == 1) {
            info[p] = v;
            return;
        }
        int m = (l + r) / 2;
        // 根据x的位置,递归更新左右子树,并合并信息
        if (x < m) {
            modify(2 * p, l, m, x, v);
        } else {
            modify(2 * p + 1, m, r, x, v);
        }
        pull(p);
    }

    // 更新函数,修改指定位置的值为给定的新值
    void modify(int p, const Info &v) {
        modify(1, 0, n, p, v);
    }

    // 区间查询函数,返回指定范围内的信息
    Info rangeQuery(int p, int l, int r, int x, int y) {
        if (l >= y || r <= x) {
            return Info();
        }
        if (l >= x && r <= y) {
            return info[p];
        }
        int m = (l + r) / 2;
        // 根据x、y的位置递归查询左右子树,并合并信息
        return merge(rangeQuery(2 * p, l, m, x, y), rangeQuery(2 * p + 1, m, r, x, y));
    }

    // 区间查询函数,返回指定范围内的信息
    Info rangeQuery(int l, int r) {
        return rangeQuery(1, 0, n, l, r);
    }
};


// 定义结构体 Info,用于存储每个节点的信息
struct Info {
    int mx1 = -1; // 最大值
    int cnt1 = 0; // 最大值出现的次数
    int mx2 = -2; // 第二大值
    int cnt2 = 0; // 第二大值出现的次数
};

// 重载+运算符,用于合并两个节点的信息
Info operator+(Info a, Info b) {
    // 如果最大值相等
    if (a.mx1 == b.mx1) {
        // 将出现次数较少的第二大值设置为第二大值,并更新出现次数
        if (a.mx2 < b.mx2) {
            std::swap(a, b);
        }
        a.cnt1 += b.cnt1;
        // 如果第二大值也相等,则合并其出现次数
        if (a.mx2 == b.mx2) {
            a.cnt2 += b.cnt2;
        }
        return a;
    }
    // 如果a的最大值小于b的最大值,交换a和b,确保a的最大值较大
    if (a.mx1 < b.mx1) {
        swap(a, b);
    }
    // 如果b的最大值大于a的第二大值,将b的最大值设为a的第二大值,并更新出现次数
    if (b.mx1 > a.mx2) {
        a.mx2 = b.mx1;
        a.cnt2 = b.cnt1;
    } else if (b.mx1 == a.mx2) {
        // 如果b的最大值等于a的第二大值,合并其出现次数
        a.cnt2 += b.cnt1;
    }
    return a;
}


int main() {
    int N, Q; cin >> N >> Q;
    vector<int> A(N);
    for (int i = 0; i < N; i++) {
        cin >> A[i];
    }
    
    SegmentTree<Info> seg(N);
    for (int i = 0; i < N; i++) {
        seg.modify(i, Info{A[i], 1, -1, 0});
    }
    
    while (Q--) {
        int o;
        cin >> o;
        
        if (o == 1) {
            int p, x;
            cin >> p >> x;
            p--;
            seg.modify(p, {x, 1, -1, 0});
        } else {
            int l, r;
            std::cin >> l >> r;
            l--;
            cout << seg.rangeQuery(l, r).cnt2 << "\n";
        }
    }
    
    return 0;
}

  

G - Compress Strings

思路:

如同题意 找到一个最短的字符串包含这n个字符串

解法:

KMP去重,在使用dp跑得出最小值即使答案

Code:

 

#include<bits/stdc++.h>

using namespace std;

// KMP 算法实现
vector<int> kmp(string s) {
    int n = s.size();
    vector<int> f(n + 1);
    for (int i = 1, j = 0; i < n; i++) {
        while (j && s[i] != s[j]) {
            j = f[j];
        }
        j += (s[i] == s[j]);
        f[i + 1] = j;
    }
    return f;
}

int main() {
    int N; // 字符串数量
    cin >> N;
    vector<string> S(N); // 存储字符串的向量
    for (auto& i : S) {
        cin >> i;
    } 
    // 按字符串长度升序排序
    sort(S.begin(), S.end(),
        [&](auto a, auto b) {
            return a.size() < b.size();
        });
    vector<string> tmp;
    // 去重处理
    for (int i = 0; i < N; i++) {
        bool ok = true;
        for (int j = i + 1; j < N; j++) {
            auto f = kmp(S[i] + '#' + S[j]); // 使用 KMP 算法判断是否存在重叠
            if (find(f.begin(), f.end(), S[i].size()) != f.end()) { // 如果存在重叠,标记为不可用
                ok = false;
                break;
            }
        }
        if (ok) { // 如果可用,则加入到临时向量中
            tmp.push_back(S[i]);
        }
    }
    S = tmp; // 更新字符串向量
    N = S.size(); // 更新字符串数量
    // for (auto& i : S) { cout << i << '\n'; }
    /* 
    abc
    arc
     */
    vector cost(N, vector<int>(N)); // 存储每对字符串之间的拼接代价
    // 计算拼接代价
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (i == j) continue;
            auto f = kmp(S[j] + '#' + S[i]); // 使用 KMP 算法找到 S[j] 的后缀匹配到 S[i] 的前缀的最大长度
            cost[i][j] = S[j].size() - f.back(); // 计算拼接代价
        }
    }
    vector dp(1 << N, vector<int>(N, 1E9)); // 定义状态数组,存储拼接代价
    for (int i = 0; i < N; i++) {
        dp[1 << i][i] = S[i].size(); // 初始化状态,表示只选择字符串 S[i] 拼接的情况
    }

    // 动态规划求解最小拼接代价
    for (int s = 1; s < (1 << N); s++) {
        for (int i = 0; i < N; i++) {
            if (s >> i & 1) {
                for (int j = 0; j < N; j++) {
                    if (~s >> j & 1) {
                        // 更新状态
                        dp[s | 1 << j][j] = min(dp[s | 1 << j][j], dp[s][i] + cost[i][j]);
                    }
                }
            }
        }
    }
    int ans = *min_element(dp.back().begin(), dp.back().end());
    cout << ans << '\n';
    return 0;
}