Edu36

1|0基本情况

尝试板刷Edu的第一天,场出三题,感觉补题困难。

2|0D. Almost Acyclic Graph

Problem - D - Codeforces

暴力的优化技巧

先考虑暴力,对每一条边进行删边,然后跑一遍判环。但是 m105O(m2) 肯定超时。

我们从拓扑排序判环的角度出发,拓扑排序通过减少一个的入度来模拟删边,而该题的点数 n500

所以直接枚举哪个点入度减一会无环即可。

void solve() { int n, m; std::cin >> n >> m; std::vector edge(n, std::vector<int>()); std::vector<int> ind(n); for (int i = 0, u, v; i < m; i++) { std::cin >> u >> v; --u, --v; edge[u].push_back(v); ind[v]++; } auto save(ind); for (int i = 0; i < n; i++) { ind = save; if (ind[i] == 0) continue; ind[i]--; std::queue<int> q; for (int i = 0; i < n; i++) { if (ind[i] == 0) { q.push(i); } } int cnt(0); while(not q.empty()) { int fr(q.front()); q.pop(); ++cnt; for (auto& to : edge[fr]) { if (--ind[to] == 0) { q.push(to); } } } if (cnt == n) {YES; return ;}//如果所有点能构成拓扑序,说明该图无环 } NO; return ; }

3|0E. Physical Education Lessons

Problem - E - Codeforces

没想到在这第一次遇到了珂朵莉树

珂朵莉树详解yzhang

电子科技大学【ACM】Chtholly Tree (珂朵莉树)

3|1板子

Willem, Chtholly and Seniorious

struct node {//维护三元组[l, r, val]表示该区间内元素相同 int l, r; mutable i64 val; node(int L, int R = -1, i64 Val = 0) { l = L, r = R, val = Val; } bool operator<(const node &a) const { return l < a.l; } }; std::set<node> S; i64 a[100005], n, m, seed, vMax; i64 rnd() { i64 ret = seed; seed = (seed * 7 + 13) % MOD; return ret; } i64 fpow(i64 x, i64 y, i64 mod) { i64 ans = 1; x %= mod; while (y) { if (y & 1) { ans = ans * x % mod; } x = x * x % mod; y >>= 1; } return ans; } auto split(int pos) {// 目的是把pos所在的点分离出来,珂朵莉树的核心 auto it = S.lower_bound(node(pos)); if (it != S.end() && it->l == pos) { return it; } it--;// 找到pos所在的区间 auto[l, r, val](*it); S.erase(it);// 把该区间的三元组删掉 S.insert(node(l, pos - 1, val)); return S.insert(node(pos, r, val)).first;// 返回pos所在区间的迭代器 // insert返回pair<iterator, bool>,所以.first } void assign(int l, int r, i64 val) {// 区间赋值 auto itR = split(r + 1), itL = split(l); S.erase(itL, itR); S.insert(node(l, r, val)); } void add(int l, int r, i64 val) {// 区间加,用左闭右开区间锁定题目需要操作的区间 auto itR = split(r + 1), itL = split(l); for (auto it = itL; it != itR; it++) { it->val += val;// 把每个区间的val都加上 } } i64 rank(int l, int r, int k) {// 求第k小,就是用最暴力的方法 std::vector<std::pair<i64, int>> vec; auto itR = split(r + 1), itL = split(l); for (auto it = itL; it != itR; it++) { vec.push_back({it->val, (it->r) - (it->l) + 1}); } sort(vec.begin(), vec.end()); for (auto[x, y] : vec) { k -= y; if (k <= 0) { return x; } } return -1; } i64 sum(int l, int r, int x, int y) {// 求幂次和 i64 ans = 0; auto itR = split(r + 1), itL = split(l); for (auto it = itL; it != itR; it++) ans = (ans + fpow(it->val, x, y) * ((it->r) - (it->l) + 1)) % y; return ans; }

3|2本题

比板子还简单,就是区间赋值的同时顺便更新总和即可

struct node {//维护三元组[l, r, val]表示该区间内元素相同 int l, r; mutable i64 val; node(int L, int R = -1, i64 Val = 0) { l = L, r = R, val = Val; } bool operator<(const node &a) const { return l < a.l; } }; std::set<node> S; i64 sum(0); auto split(int pos) {// 目的是把pos所在的点分离出来,珂朵莉树的核心 auto it = S.lower_bound(node(pos)); if (it != S.end() && it->l == pos) { return it; } it--;// 找到pos所在的区间 auto[l, r, val](*it); S.erase(it);// 把该区间的三元组删掉 S.insert(node(l, pos - 1, val)); return S.insert(node(pos, r, val)).first;// 返回pos所在区间的迭代器 // insert返回pair<iterator, bool>,所以.first } void assign(int l, int r, i64 val) {// 区间赋值 auto itR = split(r + 1), itL = split(l); for (auto it(itL); it != itR; it++) { sum -= it->val * (it->r - it->l + 1);//顺便更新sum } S.erase(itL, itR); S.insert(node(l, r, val)); sum += val * (r - l + 1);//顺便更新sum }

4|0F. Imbalance Value of a Tree

Problem - F - Codeforces

i=1nj=1nI(x,y)

其中 I(x,y) 表示顶点 xy 的简单路径上 ai 的最大值和最小值的差。

首先转化原式:

i=1ni=1nmax(i,j)i=1ni=1nmin(i,j)

其中,max(x,y) 表示顶点 xy 的简单路径中 ai 的最大值,min(x,y) 同理。

然后转化点权为边权,求最大时边权就是两点的最大值,最小时同理。

我们可以想到按照边权大小对边进行升序排序,从低到高对每一条边的两边 x,y 进行合并,并求出通过此边的路径数量 sizx×sizy,累计贡献 sizx×sizy×max(ax,ay)。注意,这里边的权值为边两端节点权值的较大值

struct DSU { std::vector<int> f, siz; DSU() {} DSU(int n) { init(n); } void init(int n) { f.resize(n); std::iota(f.begin(), f.end(), 0); siz.assign(n, 1); } int find(int x) { while (x != f[x]) { x = f[x] = f[f[x]]; } return x; } bool same(int x, int y) { return find(x) == find(y); } bool merge(int x, int y) { x = find(x); y = find(y); if (x == y) { return false; } siz[x] += siz[y]; f[y] = x; return true; } int size(int x) { return siz[find(x)]; } }; signed main() { std::cin.tie(nullptr)->sync_with_stdio(false); int n; std::cin >> n; std::vector<int> W(n); for (auto& x : W) std::cin >> x; std::vector<std::tuple<int, int, int>> edge(n - 1); for (auto&[u, v, _] : edge) { std::cin >> u >> v; --u, --v; } i64 ans(0); auto solve = [&](bool o) { DSU dsu(n); for (auto&[u, v, w] : edge) { w = o ? std::min(W[u], W[v]) : std::max(W[u], W[v]); } std::sort(all(edge), [&](auto x1, auto x2) -> bool { return o ? std::get<2>(x1) > std::get<2>(x2) : std::get<2>(x1) < std::get<2>(x2); }); for (auto&[u, v, w] : edge) {//从大到小/小到大遍历,显然先进入并查集的是对答案做出贡献的,成为祖宗 auto add(1LL * dsu.size(u) * dsu.size(v) * w); ans = o ? ans - add: ans + add; dsu.merge(u, v); } }; for (bool o : {false, true}) solve(o); std::cout << ans << '\n'; return 0; }

__EOF__

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