zoukankan      html  css  js  c++  java
  • AtCoder Beginner Contest 220【A

    比赛链接:https://atcoder.jp/contests/abc220/tasks

    A - Find Multiple

    题意

    判断 ([a, b]) 中是否有 (c) 的倍数。

    • (1 le a le b le 1000)
    • (1 le c le 1000)

    题解

    模拟。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int a, b, c;
        cin >> a >> b >> c;
        for (int i = a; i <= b; i++) {
            if (i % c == 0) {
                cout << i << "
    ";
                return 0;
            }
        }
        cout << -1 << "
    ";
        return 0;
    }
    

    B - Base K

    题意

    给出 (k) 进制下的 (a, b) ,将它们转换为十进制。

    • (2 le k le 10)
    • (1 le a, b le 10^5) (十进制下)

    题解

    模拟。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int k;
        cin >> k;
        string a, b;
        cin >> a >> b;
        int ta = 0, tb = 0;
        for (auto ch : a) {
            ta = ta * k + (ch - '0');
        }
        for (auto ch : b) {
            tb = tb * k + (ch - '0');
        }
        cout << 1LL * ta * tb << "
    ";
        return 0;
    }
    

    C - Long Sequence

    题意

    将长为 (n) 的序列 (a) 重复拼接无数次,计算前缀和第一次大于 (x) 时的位置。

    • (1 le n le 10^5)
    • (1 le a_i le 10^9)
    • (1 le x le 10^{18})

    题解

    取余。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        vector<long long> a(n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        for (int i = 1; i < n; i++) {
            a[i] += a[i - 1];
        }
        long long x;
        cin >> x;
        long long ans = 0;
        ans = x / a.back() * n;
        x %= a.back();
        for (int i = 0; i < n; i++) {
            if (a[i] > x) {
                ans += i + 1;
                break;
            }
        }
        cout << ans << "
    ";
        return 0;
    }
    

    D - FG operation

    题意

    有一个长为 (n) 的序列 (a) ,每次有两种可选操作:

    • 移去最左端的两个元素 (x, y) ,并在最左端插入 ((x + y) \% 10)
    • 移去最左端的两个元素 (x, y) ,并在最左端插入 ((x imes y) \% 10)

    问在 (2^{n - 1}) 种可能情况中,最后余下的数为 (0,1,dots,9) 的情况数,答案对 (998244353) 取模。

    • (2 le n le 10^5)
    • (0 le a_i le 9)

    题解

    (dp[i][j]) 为前 (i) 个数余下 (j) 的情况数。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    constexpr int MOD = 998244353;
    
    int norm(int x) { if (x < 0) { x += MOD; } if (x >= MOD) { x -= MOD; } return x; }
    template<class T> T binpow(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; }
    
    struct Z {
        int x;
        Z(int x = 0) : x(norm(x)) {}
        int val() const { return x; }
        Z operator-() const { return Z(norm(MOD - x)); }
        Z inv() const { assert(x != 0); return binpow(*this, MOD - 2); }
        Z &operator*=(const Z &rhs) { x = 1LL * x * rhs.x % MOD; return *this; }
        Z &operator+=(const Z &rhs) { x = norm(x + rhs.x); return *this; }
        Z &operator-=(const Z &rhs) { x = norm(x - rhs.x); return *this; }
        Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); }
        friend Z operator*(const Z &lhs, const Z &rhs) { Z res = lhs; res *= rhs; return res; }
        friend Z operator+(const Z &lhs, const Z &rhs) { Z res = lhs; res += rhs; return res; }
        friend Z operator-(const Z &lhs, const Z &rhs) { Z res = lhs; res -= rhs; return res; }
        friend Z operator/(const Z &lhs, const Z &rhs) { Z res = lhs; res /= rhs; return res; }
    };
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        vector<int> a(n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        vector<vector<Z>> dp(n, vector<Z> (10));
        dp[0][a[0]] = 1;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < 10; j++) {
                dp[i][j * a[i] % 10] += dp[i - 1][j];
                dp[i][(j + a[i]) % 10] += dp[i - 1][j];
            }
        }
        for (int i = 0; i < 10; i++) {
            cout << dp[n - 1][i].val() << "
    ";
        }
        return 0;
    }
    

    E - Distance on Large Perfect Binary Tree

    题意

    在一个 (n) 层满二叉树中,计算距离为 (d) 的结点二元组个数, ((i, j) e (j, i)) ,答案对 (998244353) 取模。

    • (2 le n le 10^6)
    • (1 le d le 2 imes 10^6)

    题解

    枚举路径所经最高层结点的左右子路径长度。

    假设分别为 (l, d - l) ,那么前 (n - max(l, d - l)) 层的结点都可以作为路径所经的最高层结点。

    易知在满二叉树中与根节点距离为 (i) 的结点的个数为 (2^i) ,且左右子树各占一半。

    所以长为 (d) 的路径个数为:

    [sum limits _{l = 0} ^{d} (2^{n - max(l, d - l)} - 1) imes 2^{max(0, l - 1)} imes 2^{max(0, d - l - 1)} ]

    最后因为问的是二元组个数,所以需要再 ( imes 2)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    constexpr int MOD = 998244353;
    
    int norm(int x) { if (x < 0) { x += MOD; } if (x >= MOD) { x -= MOD; } return x; }
    template<class T> T binpow(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; }
    
    struct Z {
        int x;
        Z(int x = 0) : x(norm(x)) {}
        int val() const { return x; }
        Z operator-() const { return Z(norm(MOD - x)); }
        Z inv() const { assert(x != 0); return binpow(*this, MOD - 2); }
        Z &operator*=(const Z &rhs) { x = 1LL * x * rhs.x % MOD; return *this; }
        Z &operator+=(const Z &rhs) { x = norm(x + rhs.x); return *this; }
        Z &operator-=(const Z &rhs) { x = norm(x - rhs.x); return *this; }
        Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); }
        friend Z operator*(const Z &lhs, const Z &rhs) { Z res = lhs; res *= rhs; return res; }
        friend Z operator+(const Z &lhs, const Z &rhs) { Z res = lhs; res += rhs; return res; }
        friend Z operator-(const Z &lhs, const Z &rhs) { Z res = lhs; res -= rhs; return res; }
        friend Z operator/(const Z &lhs, const Z &rhs) { Z res = lhs; res /= rhs; return res; }
    };
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        int d;
        cin >> d;
        Z ans = 0;
        for (int i = 0; i <= d; i++) {
            int dep = n - max(i, d - i);
            if (dep >= 1) {
                ans += (binpow(Z(2), dep) - 1) * binpow(Z(2), max(0, i - 1)) * binpow(Z(2), max(0, d - i - 1));
            }
        }
        ans *= 2;
        cout << ans.val() << "
    ";
        return 0;
    }
    

    F - Distance Sums 2

    题意

    给出一棵有 (n) 个结点的树,对于每个结点 (i) ,计算 (sum limits _{j = 1} ^{n} dis(i, j))

    • (2 le n le 10^5)

    题解

    换根 (dp) ,当根节点由父结点 (p) 转移到子结点 (u) 时,路径总和的变化量为 (-sz[u] + (n - sz[u])) ,即子结点所在子树的结点距离都 (-1) ,其余结点距离都 (+1)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        vector<vector<int>> G(n);
        for (int i = 0; i < n - 1; i++) {
            int u, v;
            cin >> u >> v;
            --u, --v;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        vector<int> sz(n), dis(n);
        function<void(int, int)> dfs1 = [&](int u, int p) {
            sz[u] = 1;
            if (p != -1) {
                dis[u] = dis[p] + 1;
            }
            for (auto v : G[u]) {
                if (v != p) {
                    dfs1(v, u);
                    sz[u] += sz[v];
                }
            }
        };
        dfs1(0, -1);
        vector<long long> ans(n);
        ans[0] = accumulate(dis.begin(), dis.end(), 0LL);
        function<void(int, int)> dfs2 = [&](int u, int p) {
            if (p != -1) {
                ans[u] = ans[p] - sz[u] + (n - sz[u]);
            }
            for (auto v : G[u]) {
                if (v != p) {
                    dfs2(v, u);
                }
            }
        };
        dfs2(0, -1);
        for (int i = 0; i < n; i++) {
            cout << ans[i] << "
    ";
        }
        return 0;
    }
    

    G - Isosceles Trapezium

    题意

    给出平面中 (n) 个不同点的整数坐标 ((x_i, y_i)) 及其权值 (c_i) ,计算可能由 (4) 个点组成的等腰梯形的最大权值。

    • (4 le n le 1000)
    • (-10^9 le x_i, y_i le 10^9)
    • (1 le c_i le 10^9)

    题解

    注意到,等腰梯形由上下两底确定,且上下两底的中点在同一条中垂线上,所以可以枚举所有的边并对其中垂线及中点建立映射。

    方便起见,采用法向式来表示每条边所对应的中垂线,即:

    [a(x - x_0) + b(y - y_0) = 0 ]

    表示过点 ((x_0, y_0)) 且与向量 ((a, b)) 垂直的直线。

    对于 ((x_i, y_i), (x_j, y_j)) 两点所在边的中垂线,易知其过中点 ((frac{x_i + x_j}{2}, frac{y_i + y_j}{2})) ,且与向量 ((x_j - x_i, y_j - y_i)) 垂直。

    为避免误差,可将整体坐标扩大 (2) 倍。

    至此,用法向式表示出了每条边的中垂线,为了便于映射,将向量进行规范化,约分后统一转为 (x ge 0)(x = 0, y ge 0) 的形式,之后将法向式化为一般式,即:

    [Ax + By + C = ax + by - (ax_0 + by_0) ]

    这样,对 ((A, B, C)) 进行映射,便确定了每条边的中垂线,之后再对中垂线所经不同点的权值进行映射即可。

    Tips

    可能存在多条边中点及中垂线相同,此时取所有点权值中的最大值。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        vector<int> x(n), y(n), c(n);
        for (int i = 0; i < n; i++) {
            cin >> x[i] >> y[i] >> c[i];
            x[i] *= 2, y[i] *= 2;
        }
        map<tuple<int, int, long long>, map<pair<int, int>, int>> mp;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int dx = x[j] - x[i], dy = y[j] - y[i];
                int g = gcd(dx, dy);
                dx /= g, dy /= g;
                if (dx < 0 or (dx == 0 and dy < 0)) {
                    dx *= -1, dy *= -1;
                }
                auto line = make_tuple(dx, dy, -(1LL * dx * (x[i] + x[j]) + 1LL * dy * (y[i] + y[j])));
                auto point = make_pair((x[i] + x[j]) / 2, (y[i] + y[j]) / 2);
                mp[line][point] = max(mp[line][point], c[i] + c[j]);
            }
        }
        long long ans = -1;
        for (auto [line, point] : mp) {
            vector<long long> v;
            for (auto [ord, c] : point) {
                v.push_back(c);
            }
            sort(v.begin(), v.end(), greater<>());
            if ((int)v.size() >= 2) {
                ans = max(ans, v[0] + v[1]);
            }
        }
        cout << ans << "
    ";
        return 0;
    }
    

    参考

    https://atcoder.jp/contests/abc220/editorial/2706

    https://atcoder.jp/contests/abc220/submissions/26150543

    https://baike.baidu.com/item/直线方程

  • 相关阅读:
    java+opencv实现图像灰度化
    java实现高斯平滑
    hdu 3415 单调队列
    POJ 3368 Frequent values 线段树区间合并
    UVA 11795 Mega Man's Mission 状态DP
    UVA 11552 Fewest Flops DP
    UVA 10534 Wavio Sequence DP LIS
    UVA 1424 uvalive 4256 Salesmen 简单DP
    UVA 1099 uvalive 4794 Sharing Chocolate 状态DP
    UVA 1169uvalive 3983 Robotruck 单调队列优化DP
  • 原文地址:https://www.cnblogs.com/Kanoon/p/15344253.html
Copyright © 2011-2022 走看看