牛客练习赛91BC 魔法学院(线段树/差分/并查集加速暴力)

编程入门 行业动态 更新时间:2024-10-27 00:30:43

牛客练习赛91BC 魔法学院(<a href=https://www.elefans.com/category/jswz/34/1769188.html style=线段树/差分/并查集加速暴力)"/>

牛客练习赛91BC 魔法学院(线段树/差分/并查集加速暴力)

B题,数据范围1e5,范围较小。
区间修改,区间查询,很明显的线段树+懒标记板子,套一块模板即可

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m, w[N];struct node {int l, r;int sum, add;
} tr[4 * N];void pushup(int u) {tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}void pushdown(int u) {auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];if (root.add) {left.add = max(root.add, left.add);right.add = max(root.add, right.add);root.add = 0;}}void build(int u, int l, int r) {if (l == r) {tr[u] = {l, r, w[r], 0};} else {tr[u] = {l, r};int mid = l + r >> 1;build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);pushup(u);}
}void modify(int u, int l, int r, int d) {if (tr[u].l >= l && tr[u].r <= r) {tr[u].add = max(d, tr[u].add);} else {pushdown(u);int mid = tr[u].l + tr[u].r >> 1;if (l <= mid)modify(u << 1, l, r, d);if (r > mid)modify(u << 1 | 1, l, r, d);pushup(u);}
}int query(int u, int l, int r) {if (tr[u].l == tr[u].r) {return max(tr[u].sum,tr[u].add);}pushdown(u);int mid = tr[u].l + tr[u].r >> 1;int sum = 0;if (l <= mid)sum = query(u << 1, l, r);if (r > mid)sum += query(u << 1 | 1, l, r);return sum;
}int main() {cin >> n >> m;for (int i = 1; i <= n; i++) {char ch;cin >> ch;w[i] = ch;}build(1, 1, n);while (m--) {int l, r;char ch;cin >> l >> r >> ch;int k = ch;modify(1, l, r, k);}cout << query(1, 1, n) << endl;
}

也可以用类差分+优先队列的方法来做,将覆盖区间的左右端点打上标记,当我们从左到右扫描时遇到开始标记,就将这个标记纳入优先队列,同时设个数组进行标记,当遇到结束标记时,就将这个魔法在标记数组中减1,同时,如果优先队列的顶部的标记消失了,就需要弹出。

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef pair<int, int>PII;
const int N = 1e5 + 10;
int n, m;
int st[200];//哪些是捏在手里的
priority_queue <char, vector<char>, less<char> >q; //现在正在用哪些东西在扫
char a[N];vector<char>gett[N], lose[N];void solve() {cin >> n >> m;for (int i = 1; i <= n; i++) {char ch;cin >> a[i];}while (m--) {int l, r;char ch;cin >> l >> r >> ch;gett[l].push_back(ch);lose[r + 1].push_back(ch);}int ans = 0;for (int i = 1; i <= n; i++) {for (auto j : gett[i]) {q.push(j);st[j]++;}for (auto j : lose[i]) {st[j]--;while (q.size() && st[q.top()] == 0) {q.pop();}}if (q.size())a[i] = max(a[i], q.top());ans += a[i];}cout << ans << endl;}signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);solve();
}

C题,数据范围1e7,线段树和差分都会爆空间,考虑用并查集加速:
将所有魔法按优先级sort一下,然后暴力填涂,如果已经被涂过了就利用并查集跳到未填涂的地方,否则就在填涂该格后并查集指向区间的右端

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
int n, m;
const int N = 1e7 + 10;int f[N];//指向一个区间里的主线
struct node {char val;int l, r;
} q[N];
char a[N];bool cmp(node a, node b) {if (a.val == b.val) {return a.l < b.l;}return a.val > b.val;
}int find(int x) {return x == f[x] ? f[x] : find(f[x]);
}void solve() {cin >> n >> m;for (int i = 1; i <= n; i++) {cin >> a[i];f[i] = i;}for (int i = 1; i <= m; i++) {cin >> q[i].l >> q[i].r >> q[i].val;}sort(q + 1, q + 1 + m, cmp);for (int i = 1; i <= m; i++) {int l = q[i].l, r = q[i].r;char ch = q[i].val;if(find(l)>r)continue;for (int i = l; i <= r; i++) {i = find(i);if (i > r)break;f[i] = max(f[i], r);a[i] = max(a[i], ch);}}int ans = 0;for (int i = 1; i <= n; i++) {ans += a[i];}cout << ans << endl;}signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);solve();
}

更多推荐

牛客练习赛91BC 魔法学院(线段树/差分/并查集加速暴力)

本文发布于:2024-02-05 09:22:20,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1674080.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:线段   暴力   差分   练习赛   学院

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!