Edu37

1|0基本情况

场出三题,C卡了很久而且不是正解。

2|0C. Swap Adjacent Elements

Problem - C - Codeforces

前缀和妙用

显然连续的 1 对应的序列是一定可以有序的。

有想到这点,但没想到合理的检查方式,所以直接用 dsu 模拟了。

2|1myCode

void solve() { int n; std::cin >> n; std::vector<int> a(n); std::string s; for (auto& x : a) std::cin >> x, x--; DSU dsu(n); std::cin >> s; for (int i = 0; i < sz(s); i++) { if (s[i] == '1') { dsu.merge(i, i + 1); } } for (int i = 0; i < n; i++) { if (not dsu.same(i, a[i])) { NO; return ; } } YES; }

2|2STD

实际上可以很直接的利用这个思路,一段连续的 1 完全可以用前缀和来检测。

当前位置的上一个和其目标位置的间隔必须全部为 1,即 aii=preai1prei1

void solve() { int n; std::cin >> n; std::vector<int> a(n); std::string s; for (auto& x : a) std::cin >> x, x--; std::cin >> s; std::vector<int> pre(n + 1); for (int i = 0; i < n; i++) { pre[i + 1] = pre[i] + (s[i] == '1'); } for (int i = 0; i < n; i++) { if (a[i] - i != pre[a[i]] - pre[i]) {NO; return ;} } YES; }

3|0E. Connected Components?

Problem - E - Codeforces

求连通块中 set 维护未遍历的点进行去重

给定一张无向图 G, 求 G的补图的连通块的个数。

这里补图就是完全图 K去掉所有 G 中的连边,也就是断掉所有原本连着的两个点,连上所有原本没有连接的两个点。记 G 的补图为 T 。如果点 s 和点 t 处于同一个连通块中,那么一定有一条路径可以从 st ,所有满足上述条件的 st 点的集合称为一个连通块。

  • 先看怎么处理补图。
    • 用一个 set 维护不该连的边,然后搜索时跳过这些边,剩下的都是该连的边。
    • 可以预见的是,暴力求这个补图的连通块时间复杂度极大: O(n2m)
  • 再考虑优化
    • 总共肯定只能访问到 n​ 个节点,瓶颈在于需要判断每一条边是否存在和是否给原来遍历过这个节点。于是考虑如何保证每一个节点和补边只会枚举到一次。
    • 再维护一个 set,这个 set 仅仅有当前没有遍历到的东西。每一次深搜遍历到某一个节点删除对应元素
      然后,每一次到一个节点遍历一整个 set 来找还可以遍历到哪里。由于每一条补边都最多给统计两次,所有在 set 里节点均摊枚举到的次数是 O(n+m),时间复杂度 O((n+m)logn),可以过。
std::set<std::pair<int, int>> unAdj;//维护补图 std::set<int> S;//维护未遍历到的点 std::set<int>::iterator dfs (int now, int& cnt) { S.erase(now); cnt++; auto it(S.begin()); while (it != S.end()) { if (unAdj.count({*it, now}) or unAdj.count({now, *it})) { ++it; } else { it = dfs(*it, cnt);//dfs后遍历过的点都跳过了 } } return S.upper_bound(now);//返回当前遍历到的最后的点的迭代器 } void solve() { int n, m; std::cin >> n >> m; for (int i = 0, u, v; i < m; i++) { std::cin >> u >> v; --u, --v; unAdj.insert({u, v}); unAdj.insert({v, u}); } for (int i = 0; i < n; i++) { S.insert(i); } std::vector<int> res; while (not S.empty()) { int cnt(0); dfs(*S.begin(), cnt); res.push_back(cnt); } std::sort(all(res)); std::cout << sz(res) << '\n'; for (auto& x : res) std::cout << x << ' '; std::cout << '\n'; }

4|0F. SUM and REPLACE

Problem - F - Codeforces

观察函数后进行分块

预处理

线段树实现

带删除的迭代器遍历操作

给定 n 个数的数组 am 次操作。操作有两种:

  1. i[l,r] 中的所有 ai 替换为 d(ai)d(x) 表示 x 的正约数的个数。
  2. i=lrai

首先通过观察发现该函数是急速收敛的。

预处理出每个数对应的 d(x)

void preprocessing() {//类似筛法原理,预处理出每个数的约数个数 for (int i = 1; i <= N; ++i) { for (int j = i; j <= N; j += i) { d[j]++; } } }

对于 1,2 这两个数而言,我们对它们修改是没有意义的,1 还是 12 还是 2

因为函数急速收敛,对于 ai 仅需几次操作之后就会变成 1,2

4|1线段树做法

我们先预处理出 di​,然后线段树维护两个值 sum, max​,在修改操作的时候如果 max​ 为 1​ 或 2​,那么跳过不修改;否则直接暴力修改即可。

const int N(1e6 + 10); int d[N]; template<class Info> struct SegmentTree { int n; std::vector<Info> info; SegmentTree() : n(0) {} SegmentTree(int n_, Info v_ = Info()) { init(n_, v_); } template<class T> SegmentTree(std::vector<T> init_) { init(init_); } void init(int n_, Info v_ = Info()) { init(std::vector(n_, v_)); } template<class T> void init(std::vector<T> init_) { n = init_.size(); info.assign(4 << std::__lg(n), Info()); std::function<void(int, int, int)> build = [&](int p, int l, int r) { if (r - l == 1) { info[p] = Info(init_[l], init_[l]); return; } int m = (l + r) / 2; build(2 * p, l, m); build(2 * p + 1, m, r); pull(p); }; build(1, 0, n); } void pull(int p) { info[p] = info[2 * p] + info[2 * p + 1]; } void rangeApply(int p, int l, int r, int x, int y) { if (l >= y or r <= x) { return; } if (info[p].max == 2 or info[p].max == 1) return ;//如果是2或者1就直接返回 if (l >= x and r <= y and r - l == 1) {//直接修改即可 info[p].apply(); return; } int m = (l + r) / 2; rangeApply(2 * p, l, m, x, y); rangeApply(2 * p + 1, m, r, x, y); pull(p); } void rangeApply(int l, int r) { return rangeApply(1, 0, n, l, r); } Info rangeQuery(int p, int l, int r, int x, int y) { if (l >= y || r <= x) { return Info(); } if (l >= x && r <= y) { return info[p]; } int m = (l + r) / 2; return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y); } Info rangeQuery(int l, int r) { return rangeQuery(1, 0, n, l, r); } }; struct Info { i64 max = -1; i64 sum = 0; Info(){} Info(int _max, int _sum): max(_max), sum(_sum) {} void apply() { max = d[max]; sum = d[sum]; } }; Info operator+(Info a, Info b) { Info c; c.max = std::max(a.max, b.max); c.sum = a.sum + b.sum; return c; } void preprocessing() {//类似筛法原理,预处理出每个数的约数个数 for (int i = 1; i <= N; ++i) { for (int j = i; j <= N; j += i) { d[j]++; } } } void solve() { preprocessing(); int n, m; std::cin >> n >> m; std::vector<int> a(n); for (auto& x : a) std::cin >> x; SegmentTree<Info> T(a); for (int i = 0; i < m; i++) { int o, l, r; std::cin >> o >> l >> r; --l; if (o == 1) { T.rangeApply(l, r); } else { std::cout << T.rangeQuery(l, r).sum << '\n'; } } }

4|2树状数组做法

用 set 维护每次需要处理的值,把当前的值小于等于 2 的都删除,然后树状数组暴力单点修改即可

template <typename T> struct Fenwick { int n; std::vector<T> a; Fenwick(int n_ = 0) { init(n_); } void init(int n_) { n = n_; a.assign(n, T{}); } void add(int x, const T &v) { for (int i = x + 1; i <= n; i += i & -i) { a[i - 1] = a[i - 1] + v; } } T sum(int x) { T ans{}; for (int i = x; i > 0; i -= i & -i) { ans = ans + a[i - 1]; } return ans; } T rangeSum(int l, int r) { return sum(r) - sum(l); } }; const int N = 1E6 + 10; int d[N]; void solve() { int n, m; std::cin >> n >> m; std::vector<i64> a(n); for (auto& x : a) std::cin >> x; for (int i = 1; i < N; i++) { for (int j = i; j < N; j += i) { d[j]++; } } Fenwick<i64> T(n); for (int i = 0; i < n; i++) { T.add(i, a[i]); } std::set<i64> S; for (int i = 0; i < n; i++) if (a[i] > 2) { S.insert(i); } for (int i = 0; i < m; i++) { int o, l, r; std::cin >> o >> l >> r; --l; if (o == 1) { auto it(S.lower_bound(l)); while(it != S.end()) {//带删除的迭代器遍历不能无脑++ int idx(*it); if (idx >= r) break;//因为[l, r),所以>=就得退出了 T.add(idx, -a[idx] + d[a[idx]]); a[idx] = d[a[idx]]; if (a[idx] <= 2) { it = S.erase(it);//删除后为了避免错乱,把当前迭代器改成删除后返回的迭代器 //erase函数返回的是删除后的下一个元素的迭代器,所以不能++了 } else { it++; } } } else { std::cout << T.rangeSum(l, r) << '\n'; } } }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/18084810.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示