zoukankan      html  css  js  c++  java
  • AtCoder Beginner Contest 214 (D并查集,E反悔贪心,F公共子序列DP)

    题目链接:Here

    ABC水题,

    D - Sum of Maximum Weights

    上图中最大权 (9) 对答案的贡献是这条边两边的连通块的 size 的乘积再乘以 9

    受到上面的启发,我们可以把每条边按边权大小从小到大排序。对于每条边(边权记为 (w)),先求出当前边连接的两个 group 的 size,不妨记为 (size_a)(size_b) ,再把 (size_a imes size_b imes w)​​ 累加后合并两个连通块(并查集)

    这里偷懒用一下 atcoder 的库函数写。

    #include <bits/stdc++.h>
    #include <atcoder/all>
    using namespace std;
    using namespace atcoder;
    
    int main() {
        int n;
        cin >> n;
        vector<tuple<long long, int, int>> p(n - 1);
        for (auto &[w, u, v] : p) {
            cin >> u >> v >> w;
            u--; v--;
        }
        sort(p.begin(), p.end());
        long long ans = 0;
        dsu uf(n);
        for (auto [w, u, v] : p) {
            if (!uf.same(u, v)) {
                ans += w * uf.size(u) * uf.size(v);
                uf.merge(u, v);
            }
        }
        cout << ans << endl;
        return 0;
    }
    

    E - Packing Under Range Regulations

    题意理解来自 Ncik桑

    本题显然是区间调度问题(反悔贪心问题),和以下问题等价:

    • (N) 个工作。 第 (i) 个工作可以从 (L_i) 日开始,截止日期为 (R_i) 日。 任何一项工作都可以在一天内完成,一天最多只能完成一项工作。 你能在截止日期前完成所有工作吗?

    显而易见的,我们应该从最紧急的工作开始,即把任务按 (L) 从大到小排列然后用优先级队列按 (R) 的大小顺序检索 “你现在可以做的任务 “来模拟这种情况。

    const int inf = 1001001001;
    void solve() {
        int n; cin >> n;
        vector<pair<int, int>> a(n);
        for (auto &[u, v] : a) cin >> u >> v;
        sort(a.begin(), a.end());
        priority_queue<int, vector<int>, greater<int>>q;
        int x = 1;
        a.push_back({inf, inf});
        for (auto [l, r] : a) {
            while (x < l && q.size()) {
                if (q.top() < x) {
                    cout << "No
    ";
                    return ;
                }
                q.pop(); x += 1;
            }
            x = l; q.push(r);
        }
        cout << "Yes
    ";
    }
    

    F - Substrings

    首先,让我们考虑不受相邻字符不同时选择的约束的问题。

    查找S的非空子字符串的数目。在这里,子字符串是在删除0个或更多字符的情况下不重新排序原始字符串的串联。

    在这里,重要的是不同的删除方式可能会导致相同的子字符串。这里会用“公共子序列DP”的方法解决问题,在该方法中,子字符串的计数不包含那些重复项。
    考虑下面的DP。

    考虑下面的DP

    • (dp_i):= 字符串中第 (1) 个到第 (i) 个字符串的数目,

    定义 (dp_{p_0} = 1) 对应于一个空字符串。转换可以写为以下内容:

    • (dp_i = sum_{j = 0}^{i - 1}dp_j)

    但可能会多次计算相同子字符串,所以稍微修改一下

    • (dp_i = sum_{j = k}^{i - 1}dp_j) ,其中 (k) 为最大整数使得 (S_i = S_k (k < i)) 如果没有这样的整数则 (k = 0)

    直观地说,如果 (S_k = S_i)​ ,那么我们禁止在某些 (j(<k))(S_j) 后面追加 (S_i) ,因为这没有意义(我们可以在 (S_j) 后面追加 (S_k)),这样就避免了重复。事实上,这是计算所有不重复的子字符串所需的唯一扭曲。

    乍一看,复杂度看起来像 (mathcal{O}(|S|^2)),但实际上,借助累积和,它可以总共执行 (mathcal{O}(|S|)),或者在没有累积和的情况下执行 (mathcal{O}(σ|S|)),其中 (σ)​ 表示不同字母的数量,这足够快了。

    这个想法也可以应用于原始问题。设 (dp_0 = 1)(dp_1=0)

    递归关系可以写成:(dp_{i+1} = sum_{j = k}^{i - 1}dp_j)​ ,其中 (k)​ 是最大整数,使得 (S_i=S_k)​ 和 (k<i)(如果没有这样的整数,则 (k=0)

    const int mod = 1e9 + 7;
    int main() {
        cin.tie(nullptr)->sync_with_stdio(false);
        string s; cin >> s;
        int n = s.size();
        vector<ll> f(n + 2); f[0] = 1;
        for (int i = 0; i < n; ++i)
            for (int j = i - 1;; j--) {
                f[i + 2] = (f[i + 2] + f[j + 1]) % mod;
                if (j == -1 || s[j] == s[i]) break;
            }
        ll ans = 0;
        for (int i = 2; i < n + 2; i++) ans += f[i];
        cout << ans % mod << "
    ";
    }
    

    The desire of his soul is the prophecy of his fate
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    ffmpeg 转换VC工具已经可以生成工程文件
    ffmpeg 转换VC工具已经可以生成工程文件(续)
    ffmpeg 转换VC工具 V1.1.1
    ffmpeg 转换VC工具已经可以生成工程文件(续)
    ffmpeg 工程代码半自动转换vc工具
    ffmpeg 转换VC工具 V1.1.1
    lua 解析ffmpeg结构体时候用的正则表达式
    ffmpeg 工程代码半自动转换vc工具
    lua 解析ffmpeg结构体时候用的正则表达式
    ffmpeg 转换VC工具已经可以生成工程文件
  • 原文地址:https://www.cnblogs.com/RioTian/p/15143685.html
Copyright © 2011-2022 走看看