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
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    SharePoint 2010 User Profile Sync Service自动停止
    如何区别多个svchost.exe?
    Log Parser分析IIS log的一个简单例子
    Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
    Windows中右键点击文件夹, 结果找不到共享选项卡, 怎么办?
    介绍SOS中的SaveModule命令
    SharePoint中Draft版本的文档不会收到document added的Alert Email
    和我一起学Windows Workflow Foundation(1)创建和调试一个WF实例
    门户网站
    C#基础—— check、lock、using语句归纳
  • 原文地址:https://www.cnblogs.com/RioTian/p/15143685.html
Copyright © 2011-2022 走看看