题目链接:Here

ABC水题,

D - Sum of Maximum Weights

AtCoder Beginner Contest 214 - 图1

上图中最大权 AtCoder Beginner Contest 214 - 图2 对答案的贡献是这条边两边的连通块的 size 的乘积再乘以 9

受到上面的启发,我们可以把每条边按边权大小从小到大排序。对于每条边(边权记为 AtCoder Beginner Contest 214 - 图3),先求出当前边连接的两个 group 的 size,不妨记为 AtCoder Beginner Contest 214 - 图4AtCoder Beginner Contest 214 - 图5 ,再把 AtCoder Beginner Contest 214 - 图6 累加后合并两个连通块(并查集)

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

  1. #include <bits/stdc++.h>
  2. #include <atcoder/all>
  3. using namespace std;
  4. using namespace atcoder;
  5. int main() {
  6. int n;
  7. cin >> n;
  8. vector<tuple<long long, int, int>> p(n - 1);
  9. for (auto &[w, u, v] : p) {
  10. cin >> u >> v >> w;
  11. u--; v--;
  12. }
  13. sort(p.begin(), p.end());
  14. long long ans = 0;
  15. dsu uf(n);
  16. for (auto [w, u, v] : p) {
  17. if (!uf.same(u, v)) {
  18. ans += w * uf.size(u) * uf.size(v);
  19. uf.merge(u, v);
  20. }
  21. }
  22. cout << ans << endl;
  23. return 0;
  24. }

E - Packing Under Range Regulations

题意理解来自 Ncik桑

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

  • AtCoder Beginner Contest 214 - 图7 个工作。 第 AtCoder Beginner Contest 214 - 图8 个工作可以从 AtCoder Beginner Contest 214 - 图9 日开始,截止日期为 AtCoder Beginner Contest 214 - 图10 日。 任何一项工作都可以在一天内完成,一天最多只能完成一项工作。 你能在截止日期前完成所有工作吗?

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

  1. const int inf = 1001001001;
  2. void solve() {
  3. int n; cin >> n;
  4. vector<pair<int, int>> a(n);
  5. for (auto &[u, v] : a) cin >> u >> v;
  6. sort(a.begin(), a.end());
  7. priority_queue<int, vector<int>, greater<int>>q;
  8. int x = 1;
  9. a.push_back({inf, inf});
  10. for (auto [l, r] : a) {
  11. while (x < l && q.size()) {
  12. if (q.top() < x) {
  13. cout << "No\n";
  14. return ;
  15. }
  16. q.pop(); x += 1;
  17. }
  18. x = l; q.push(r);
  19. }
  20. cout << "Yes\n";
  21. }

F - Substrings

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

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

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

考虑下面的DP

  • AtCoder Beginner Contest 214 - 图13:= 字符串中第 AtCoder Beginner Contest 214 - 图14 个到第 AtCoder Beginner Contest 214 - 图15 个字符串的数目,

定义 AtCoder Beginner Contest 214 - 图16 对应于一个空字符串。转换可以写为以下内容:

  • AtCoder Beginner Contest 214 - 图17

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

  • AtCoder Beginner Contest 214 - 图18 ,其中 AtCoder Beginner Contest 214 - 图19 为最大整数使得 AtCoder Beginner Contest 214 - 图20#card=math&code=S_i%20%3D%20S_k%5C%20%28k%20%3C%20i%29) 如果没有这样的整数则 AtCoder Beginner Contest 214 - 图21

直观地说,如果 AtCoder Beginner Contest 214 - 图22 ,那么我们禁止在某些 AtCoder Beginner Contest 214 - 图23#card=math&code=j%28%3Ck%29)的 AtCoder Beginner Contest 214 - 图24 后面追加 AtCoder Beginner Contest 214 - 图25 ,因为这没有意义(我们可以在 AtCoder Beginner Contest 214 - 图26 后面追加 AtCoder Beginner Contest 214 - 图27),这样就避免了重复。事实上,这是计算所有不重复的子字符串所需的唯一扭曲。

乍一看,复杂度看起来像 AtCoder Beginner Contest 214 - 图28#card=math&code=%5Cmathcal%7BO%7D%28%7CS%7C%5E2%29),但实际上,借助累积和,它可以总共执行 AtCoder Beginner Contest 214 - 图29#card=math&code=%5Cmathcal%7BO%7D%28%7CS%7C%29),或者在没有累积和的情况下执行 AtCoder Beginner Contest 214 - 图30#card=math&code=%5Cmathcal%7BO%7D%28%CF%83%7CS%7C%29),其中 AtCoder Beginner Contest 214 - 图31 表示不同字母的数量,这足够快了。

这个想法也可以应用于原始问题。设 AtCoder Beginner Contest 214 - 图32AtCoder Beginner Contest 214 - 图33

递归关系可以写成:AtCoder Beginner Contest 214 - 图34 ,其中 AtCoder Beginner Contest 214 - 图35 是最大整数,使得 AtCoder Beginner Contest 214 - 图36AtCoder Beginner Contest 214 - 图37(如果没有这样的整数,则 AtCoder Beginner Contest 214 - 图38

  1. const int mod = 1e9 + 7;
  2. int main() {
  3. cin.tie(nullptr)->sync_with_stdio(false);
  4. string s; cin >> s;
  5. int n = s.size();
  6. vector<ll> f(n + 2); f[0] = 1;
  7. for (int i = 0; i < n; ++i)
  8. for (int j = i - 1;; j--) {
  9. f[i + 2] = (f[i + 2] + f[j + 1]) % mod;
  10. if (j == -1 || s[j] == s[i]) break;
  11. }
  12. ll ans = 0;
  13. for (int i = 2; i < n + 2; i++) ans += f[i];
  14. cout << ans % mod << "\n";
  15. }