AtCoder Beginner Contest 215【A - G】

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

A - Your First Judge

题意

如果一个字符串是 Hello,World! ,输出 AC ,否则输出 WA

题解

模拟。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    string s;
    cin >> s;
    cout << (s == "Hello,World!" ? "AC" : "WA") << "\n";
    return 0;
}

B - log2(N)

题意

输出 \(2\) 不超过 \(n\) 的最大幂次方。

题解

模拟。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    long long n;
    cin >> n;
    cout << __lg(n) << "\n";
    return 0;
}

C - One More aab aba baa

题意

给出一个字符串,输出其所含有的字母对应的字典序第 \(k\) 小的字符串。

题解

模拟。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    string s;
    int k;
    cin >> s >> k;
    sort(s.begin(), s.end());
    int cnt = 0;
    do {
        if (++cnt == k) {
            cout << s << "\n";
            break;
        }
    } while (next_permutation(s.begin(), s.end()));
    return 0;
}

D - Coprime 2

题意

给出一个大小为 \(n\) 的数组 \(a\) ,找出 \([1, m]\) 中与每个 \(a_i\)\(gcd=1\) 的数。

题解

把每个 \(a_i\) 做一下质因子分解然后用这些质因子筛一下 \([1, m]\) 即可。

代码

#include <bits/stdc++.h>
using namespace std;
constexpr int N = 1e5 + 10;

int p[N];
void Init() {
    for (int i = 2; i < N; i++) {
        if (p[i]) {
            continue;
        }
        for (int j = i; j < N; j += i) {
            p[j] = i;
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    Init();
    int n, m;
    cin >> n >> m;
    vector<int> a(n);
    set<int> st;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        for (int j = a[i]; j != 1; j /= p[j]) {
            st.insert(p[j]);
        }
    }
    vector<bool> is(N, true);
    for (auto i : st) {
        if (not is[i] or i == 1) {
            continue;
        }
        for (int j = i; j < N; j += i) {
            is[j] = false;
        }
    }
    vector<int> ans;
    for (int i = 1; i <= m; i++) {
        if (is[i]) {
            ans.push_back(i);
        }
    }
    cout << ans.size() << "\n";
    for (auto i : ans) {
        cout << i << "\n";
    }
    return 0;
}

E - Chain Contestant

题意

给出一个长为 \(n\) 的字符串 \(s\) ,计算共有多少子序列满足相同字母在原串中连续。

题解

将字符逐个考虑,每次它需要加上之前所有不含它或以它结尾的序列。

模拟代码即:

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    string s;
    cin >> n >> s;
    map<string, int> mp;
    mp[""] = 1;
    for (auto ch : s) {
        auto next_mp(mp);
        for (auto [x, y] : mp) {
            if (x.find(ch) == string::npos) {
                next_mp[x + ch] += mp[x];
            } else if (x.back() == ch) {
                next_mp[x] += mp[x];
            }
        }
        mp = next_mp;
    }
    int ans = 0;
    for (auto [x, y] : mp) {
        if (x != "") {
            ans += y;
        }
    }
    cout << ans << "\n";
    return 0;
}

但最多有 \(2^{|s| = 1000}\) 种子序列,考虑利用题目条件压缩。

因为最多有 \(10\) 个字母,所以可以枚举字母的所有排列组合方式,同时连续的相同字母都可以看作一个字母,所以最终共可能有 \(\sum \limits _{i = 1}^{10} C_{10}^i \cdot A_i^i \approx 10^7\) 种序列。

\(|s| \cdot 10^7 = 10^{10}\) 的复杂度仍难以接受(事实上这份代码跑了 \(20s\) ),考虑进一步压缩。

因为只需要知道一个序列是否含有 \(ch\) 或以 \(ch\) 结尾,所以可以只保存序列的字母集和结尾字母,此时复杂度为 \(|s| \cdot 2^{10} \cdot 10 \approx 10^7\)

\(dp_{ij}\) 为字母集为 \(i\) ,以字母 \(j\) 结尾的序列个数,将上部分代码改写一下即可。

代码

#include <bits/stdc++.h>
using namespace std;
constexpr int N = 1 << 10;
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;
    string s;
    cin >> n >> s;
    vector<vector<Z>> dp(N, vector<Z> (10));
    for (auto ch : s) {
        auto next_dp(dp);
        int id = ch - 'A';
        for (int i = 0; i < N; i++) {
            if ((i & (1 << id)) == 0) {
                next_dp[i | (1 << id)][id] += (i == 0 ? 1 : accumulate(dp[i].begin(), dp[i].end(), Z(0)));
            } else {
                next_dp[i][id] += dp[i][id];
            }
        }
        dp = next_dp;
    }
    Z ans = 0;
    for (auto vec : dp) {
        for (auto x : vec) {
            ans += x;
        }
    }
    cout << ans.val() << "\n";
    return 0;
}

F - Dist Max 2

题意

给出平面上的 \(n\) 个整数点,定义两点间的距离为 \(\mathrm{min} (|x_i-x_j|,|y_i-y_j|)\) ,找出两个不同点间的最大距离。

题解

The maximization problem of a minimum value can often be solved with a binary search.

——Editorial

考虑二分最终答案,将所有点按 \(x\) 坐标从小到大排序,然后记录满足 \(x - pre_x \ge mid\)\(pre_y\) 的最值,若存在 \(y - pre_{y\_{min}}\)\(pre_{y\_{max}} - y \ge mid\) ,则该二分值可行,设为下界。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<pair<int, int>> a(n);
    for (auto& [x, y] : a) {
        cin >> x >> y;
    }
    sort(a.begin(), a.end());
    int l = 0, r = 1e9;
    while (l < r) {
        int mid = (l + r + 1) / 2;
        bool ok = false;
        queue<pair<int, int>> que; 
        int mi = 1e9, mx = 0;
        for (auto [x, y] : a) {
            while (not que.empty() and x - que.front().first >= mid) {
                mi = min(mi, que.front().second);
                mx = max(mx, que.front().second);
                que.pop();
            }
            if (y - mi >= mid or mx - y >= mid) {
                ok = true;
            }
            que.emplace(x, y);
        }
        if (ok) {
            l = mid;
        } else {
            r = mid - 1;
        }
    }
    cout << l << "\n";
    return 0;
}

G - Colorful Candies 2

题意

\(n\) 个带颜色的糖果,计算当 \(k = 1, 2, \dots, n\) 时, \(C_{n}^{k}\) 种情况中糖果颜色数的期望。

题解

出现 \(c_i\) 次的颜色对总期望的贡献为 \(1 \times \frac{C_n^k - C_{n - c_i}^k}{C_n^k}\)

但逐颜色计算时间复杂度为 \(O_{(n^2)}\) ,考虑进行压缩:对于有相同出现次数 \(c_i\) 的颜色,可以合并计算为 \(tot \times \frac{C_n^k - C_{n - c_i}^k}{C_n^k}\) ,此时时间复杂度为 \(O_{(n \sqrt{n})}\)

证明

假设出现次数都不相同,因为 \(\sum c_i = n = 1 + 2 + \dots +m\) ,即 \(\frac{m(m + 1)}{2} = n\) ,易得 \(m \le \sqrt{n}\) ,所以此时时间复杂度为 \(O_{(n \sqrt{n})}\)

代码

#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; }
};

struct comb {
    vector<Z> fac, inv;

    comb(int n) : fac(n), inv(n) {
        fac[0] = 1;
        for (int i = 1; i < n; i++) fac[i] = fac[i - 1] * i;
        inv[n - 1] = fac[n - 1].inv();
        for (int i = n - 2; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1);
    }

    Z C(int n, int m){
        if(m < 0 or m > n) return 0;
        return fac[n] * inv[m] * inv[n - m];
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<int> c(n);
    for (int i = 0; i < n; i++) {
        cin >> c[i];
    }
    auto disc = [](vector<int> res) {
        vector<int> a(res);
        sort(a.begin(), a.end());
        a.resize(unique(a.begin(), a.end()) - a.begin());
        for (auto& i : res) {
            i = lower_bound(a.begin(), a.end(), i) - a.begin();
        }
        return res;
    };
    auto zip = [](vector<int> c, int n) {
        vector<int> color(c.size());
        for (auto i : c) {
            ++color[i];
        }
        vector<int> tot(n + 1);
        for (auto i : color) {
            ++tot[i];
        }
        vector<pair<int, int>> res;
        for (int i = 1; i <= n; i++) {
            if (tot[i]) {
                res.emplace_back(tot[i], i);
            }
        }
        return res;    
    };
    vector<pair<int, int>> v = zip(disc(c), n);
    comb comb(n + 1);
    for (int k = 1; k <= n; k++) {
        Z ans = 0;
        for (auto [tot, c_i] : v) {
            ans += tot * (comb.C(n, k) - comb.C(n - c_i, k)) * comb.C(n, k).inv();
        }
        cout << ans.val() << "\n";
    }
    return 0;
}

参考

https://atcoder.jp/contests/abc215/editorial/2514

https://atcoder.jp/contests/abc215/editorial/2515

https://atcoder.jp/contests/abc215/editorial/2516

posted @ 2021-08-22 01:15  Kanoon  阅读(159)  评论(2编辑  收藏  举报