当前位置: 首页 > news >正文

LG P7447 [Ynoi2007] rgxsxrs Solution

Description

给定序列 a=(a1,a2,⋯ ,an)a=(a_1,a_2,\cdots,a_n)a=(a1,a2,,an),有 mmm 个操作分两种:

  • subtract⁡(l,r,x)\operatorname{subtract}(l,r,x)subtract(l,r,x):对每个 i∈[l,r]i\in[l,r]i[l,r] 执行 ai←ai−x×[ai>x]a_i\gets a_i-x\times [a_i>x]aiaix×[ai>x].
  • query⁡(l,r)\operatorname{query}(l,r)query(l,r):求 ∑i=lrai,min⁡i=lrai,max⁡i=lrai\sum\limits_{i=l}^r a_i,\min\limits_{i=l}^r a_i,\max\limits_{i=l}^r a_ii=lrai,i=lminrai,i=lmaxrai.

Limitations

1≤n,m≤5×1051\le n,m\le 5\times 10^51n,m5×105
1≤ai,x≤1091\le a_i,x\le 10^91ai,x109
1≤l≤r≤n1\le l\le r\le n1lrn
6s,64MB6\text{s},\textcolor{red}{64\text{MB}}6s,64MB

Solution

首先考虑一种势能线段树:

每个节点维护所管辖区间的最值,在执行 subtract⁡\operatorname{subtract}subtract 时:

  • 若当前 max⁡<x\max< xmax<x,操作没有用,直接退出.
  • 若当前 min⁡>x\min> xmin>x,所有数都会被改,给当前节点打上一个减去 xxx 的标记.

但是这玩意的复杂度是错的,只要给一个 1,1091,10^91,109 交替的序列,每次 subtract⁡(1,n,1)\operatorname{subtract}(1,n,1)subtract(1,n,1) 就被卡成 O(nm)O(nm)O(nm) 了.

序列分块似乎也做不了,只能对值域分块,由于 VVV 可达 10910^9109,所以要用倍增分块,将 [bi,bi+1)[b^i,b^{i+1})[bi,bi+1) 分为一块,总共有 ⌈log⁡bV⌉\lceil \log_b V\rceillogbV 块,其中 bbb 为底数.

对每个块维护一棵上面所述的线段树,容易发现每个数最多减 bbb 次就会掉出该块管辖范围,至于这些掉出的元素,因为一个数最多掉出 O(log⁡bV)O(\log_b V)O(logbV) 次,可以直接暴力找块插入.

这样做的时间复杂度为 O(nblog⁡nlog⁡bV)O(nb\log n\log_b V)O(nblognlogbV),空间复杂度为 O(nlog⁡bV)O(n \log_b V)O(nlogbV).
然而由于常数比较大,bbb222 是不够的,需要调大.

空间限制很紧,但是强制在线无法逐块处理,注意到线段树的底下几层很浪费空间,于是若节点的长度小于一个阈值 LLL 时,就直接暴力处理.

bbbLLL 需要根据自己的代码情况调,笔者取的是 b=32,L=32b=32,L=32b=32,L=32.

还有其他细节需要注意:

  • 线段树节点不要存 l,rl,rl,r,容易被卡.
  • 底层暴力时处理插入时,不要给新插入的元素错误地打上标记.
  • 区间的元素不再是 r−l+1r-l+1rl+1 个,需要维护 cnt\textit{cnt}cnt 表示元素个数.
  • 由于所有线段树底层共用一个 aaa,需要先判断是否在本块范围内再操作.

Code

6.83KB,4.37s,12.50MB  (in total, C++20 with O2)6.83\text{KB},4.37\text{s},12.50\text{MB}\;\texttt{(in total, C++20 with O2)}6.83KB,4.37s,12.50MB(in total, C++20 with O2)
只给主要部分.

namespace block {struct data {i64 sum;int max, min, cnt;inline data() : sum(0), max(0), min(inf), cnt(0) {}inline data(i64 _sum, int _max, int _min, int _cnt): sum(_sum), max(_max), min(_min), cnt(_cnt) {}};inline data operator+(const data& a, const data& b) {return data(a.sum + b.sum,std::max(a.max, b.max),std::min(a.min, b.min),a.cnt + b.cnt);}inline int ls(int u) { return 2 * u + 1; }inline int rs(int u) { return 2 * u + 2; }struct segment_tree {int n, bl, br;vector<data> info;vector<int> tag;vector<int>::iterator a;vector<bool>::iterator fall;inline segment_tree() {}inline void init(int _n, int _bl, int _br) {n = _n, bl = _bl, br = _br;const int cap = std::max(1, n >> 3);info.resize(cap), tag.resize(cap);}inline void leaf_remove(int u, int l, int r, int ql, int qr, int v, vector<int>& trash) {data res;for (int i = l; i <= r; i++)if (bl <= a[i] && a[i] <= br) {a[i] -= tag[u];if (ql <= i && i <= qr && a[i] > v) {a[i] -= v;if (a[i] < bl) {trash.push_back(i);fall[i] = true;continue;}}res = res + data(a[i], a[i], a[i], 1);}info[u] = res;tag[u] = 0;}inline void leaf_insert(int u, int l, int r) {data res;for (int i = l; i <= r; i++)if (bl <= a[i] && a[i] <= br) {if (!fall[i]) a[i] -= tag[u];else fall[i] = false;res = res + data(a[i], a[i], a[i], 1);}info[u] = res;tag[u] = 0;}inline data leaf_query(int u, int l, int r, int ql, int qr) {data res;for (int i = l; i <= r; i++)if (bl <= a[i] && a[i] <= br) {a[i] -= tag[u];if (ql <= i && i <= qr)res = res + data(a[i], a[i], a[i], 1);}tag[u] = 0;return res;}inline void pushup(int u) {info[u] = info[ls(u)] + info[rs(u)];}inline void apply(int u, int k) {tag[u] += k;info[u].sum -= 1LL * info[u].cnt * k;info[u].max -= k, info[u].min -= k;}inline void pushdown(int u) {if (tag[u]) {apply(ls(u), tag[u]);apply(rs(u), tag[u]);tag[u] = 0;}}void build(int u, int l, int r) {if (r - l < L) return void(info[u] = leaf_query(u, l, r, l, r));const int mid = (l + r) >> 1;build(ls(u), l, mid);build(rs(u), mid + 1, r);pushup(u);}void insert(int u, int l, int r, int i) {if (r - l < L) return leaf_insert(u, l, r);const int mid = (l + r) >> 1;pushdown(u);if (i <= mid) insert(ls(u), l, mid, i);else insert(rs(u), mid + 1, r, i);pushup(u);}void modify(int u, int l, int r, int ql, int qr, int v, vector<int>& trash) {if (info[u].max <= v) return;if (ql <= l && r <= qr && info[u].min - v >= bl) return apply(u, v);if (r - l < L) return leaf_remove(u, l, r, ql, qr, v, trash);const int mid = (l + r) >> 1;pushdown(u);if (ql <= mid) modify(ls(u), l, mid, ql, qr, v, trash);if (qr > mid) modify(rs(u), mid + 1, r, ql, qr, v, trash);pushup(u);}data query(int u, int l, int r, int ql, int qr) {if (ql <= l && r <= qr) return info[u];if (r - l < L) return leaf_query(u, l, r, ql, qr);const int mid = (l + r) >> 1;pushdown(u);if (qr <= mid) return query(ls(u), l, mid, ql, qr);else if (ql > mid) return query(rs(u), mid + 1, r, ql, qr);else return query(ls(u), l, mid, ql, qr) + query(rs(u), mid + 1, r, ql, qr);}inline void insert(int i) {insert(0, 0, n - 1, i);}inline void subtract(int l, int r, int v, vector<int>& trash) {modify(0, 0, n - 1, l, r, v, trash);}inline data query(int l, int r) {return query(0, 0, n - 1, l, r);}inline void build() {build(0, 0, n - 1);}};struct blocks {int n;vector<segment_tree> sgt;vector<int> trash;vector<bool> fall;vector<int>::iterator a;inline blocks() {}inline blocks(int _n) : n(_n) {}inline void init(vector<int>& a) {sgt.resize(B + 1);fall.resize(n);for (int j = 0; j <= B; j++) {const int bl = 1 << (j * B);const int br = (1 << ((j + 1) * B)) - 1;sgt[j].init(n, bl, br);sgt[j].a = a.begin();sgt[j].fall = fall.begin();sgt[j].build();}this->a = a.begin();}inline void subtract(int l, int r, int x) {for (int j = 0; j <= B; j++) {sgt[j].subtract(l, r, x, trash);if (j > 0) {for (auto i : trash) for (int k = 0; k < j; k++)if (sgt[k].bl <= a[i] && a[i] <= sgt[k].br) {sgt[k].insert(i);break;}trash.clear();}}}inline data query(int l, int r) {data res;for (int j = 0; j <= B; j++) res = res + sgt[j].query(l, r);return res;}};
}
http://www.lryc.cn/news/613626.html

相关文章:

  • 树莓派安装OpenCV环境
  • 代码库详细笔记
  • 使用 Tauri 开发 Android 应用:环境搭建与入门指南
  • 进程间数据的关联与隔离
  • Next.js 15 重磅发布:React 19 集成 + 性能革命,开发者必看新特性指南
  • 代码随想录day58图论8
  • 一个设备或系统能够同时管理和监控两个摄像头的配
  • Ethereum: 像Uniswap V3贡献者一样开发,克隆、编译与测试v3-core
  • 【Unity Plugins】使用Magica Cloth 2 实现头发和服饰的效果模拟
  • 职责链模式应用场景与C++实现
  • 前端开发工具大全
  • 大疆前端笔试题目详解
  • PostgreSQL 强制索引:当重复数据让优化器“失明”时的解决方案
  • 实验室课程|基于SprinBoot+vue的实验室课程管理系统(源码+数据库+文档)
  • vue3 el-select 加载内容后 触发事件
  • Mysql自定义顺序查询
  • Mysql 单行函数 聚合函数
  • 六类注定烂尾的甲方软件外包必看!这类甲方不要理-优雅草卓伊凡
  • sigprocmask 函数深度解析
  • 【指南版】网络与信息安全岗位系列(三):安全运维工程师
  • Redis 分布式Session
  • Redis面试精讲 Day 16:Redis性能监控与分析工具
  • 锡膏种类多,不同的锡膏有什么区别,该如何正确选择?
  • 深入理解 ReentrantLock和AQS底层源码
  • Day09 Tlisa登录认证
  • 计算机英语详细总结
  • 类和对象(中):类的默认成员函数、构造函数、析构函数
  • MinHash算法:为什么选择Min而不是Max
  • DM数据库集群操作顺序规范
  • Linux线程学习