P4690 [Ynoi Easy Round 2016] 镜中的昆虫 吃shi日记

news/2025/2/21 22:11:01/文章来源:https://www.cnblogs.com/SimonHTC/p/18726487

题目大意

给定长度为 \(n\) 的颜色序列 \(a\),需要维护两种操作:

  • 给定 \(l,r,c\),将区间 \([l,r]\) 内的颜色全部覆盖为 \(c\)
  • 给定 \(l,r\),查询区间 \([l,r]\) 内不同颜色的种类数;

题解

首先考虑单修怎么做。

数颜色有一种常用的技巧:维护 \(pre[i]\) 表示 \(a[i]\) 左侧和它颜色相同的最靠右的位置。修改时,我们使用 set 维护每一种颜色出现的所有位置,找到发生改变的 \(O(1)\)pre,进行修改即可;查询时,我们只需要查询 \([l,r]\) 中有多少个 \(pre[i]<pre_0=l\)。这是一类在线二维数点问题,可以离线并使用 cdq 分治解决(即不改变操作原有的时间顺序情况下,对操作的 \(pre_0\) 进行分治,并用树状数组维护序列下标维度的区间和)。

接下来考虑区修怎么做。这里引入颜色段均摊的经典结论:

对一个长度为 \(n\) 的颜色序列进行 \(m\) 次区间赋值,pre 数组的改变次数为 \(O(n+m)\)(不计常数)。

这是因为,我们可以将颜色相同的一个段看成一个点,区修 \([l,r]\) 就是把 \([l,r]\) 包含的所有段都删掉,然后插入 \([l,r]\)(如果 \(l\)\(r\) 落在某一个段的内部,则将这个段劈成两半再操作即可)。

每次将一个颜色段 \([l,r]\) 删除,最多会改变两个位置的 \(pre\):左端点 \(l\) 和右侧第一个同色点 \(nxt[r]\)。每次执行区间覆盖 \([l,r]\) 时最多插入 \(3\) 个颜色段,插入时会修改 \(l\)\(nxt[r]\) 两个位置的 pre,每个颜色段最多被删除一次,因此修改操作不超过 \(2(n+3m)+2m=10\times MAXN=O(n+m)\) 个。

我们使用 ODT 维护颜色段,在 assign() 内产生对 pre 的修改操作,并按照时间顺序和查询操作混合送入 cdq 中处理即可。

卡常

lxl 不愧是毒瘤,64M 内存限制。。。

主要针对 cdq 中操作序列的传递进行优化。直接传 vector<Op> & 肯定不行。那每次传 \([ql,qr]\) 表示操作序列为 op[ql:qr],可以减少栈的消耗。这就需要使用辅助数组存储拆分的结果。为了进一步减少内存,辅助数组不能定义为 Op 类型,只存储下标即可。

具体的,我们定义两个数组 swp[i]tmp[i],分别用来记录操作序列的实际顺序和拆分结果。初始时 swp[i]=icdq 内我们将分到左侧区间的操作的 swp 先装入 tmp,右侧区间的操作排列在其后面,然后从 tmp 复制回 swp 即可。

空间 \(63.48MB\),时间 \(8.58s\)

点击查看代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#define cint const int &
using namespace std;
const int N = 1E5 + 10;struct Query {int tp, l, r, x;inline Query(cint _tp = 0, cint _l = 0, cint _r = 0, cint _x = 0) :tp(_tp), l(_l), r(_r), x(_x) {}
};struct Op {int tp, l, r, v, w, id;
};struct Node {int l, r, c;inline Node(int _l, int _r, int _c) :l(_l), r(_r), c(_c) {}inline bool operator<(const Node &b) const {return l < b.l;}
};int n, m;
int a[N], pre[N], ans[N];
Query q[N];
Op op[11 * N];
int swp[11 * N], tmp[11 * N], opCnt;int num[2 * N], nn;void lisanhua() {sort(num + 1, num + 1 + nn);nn = unique(num + 1, num + 1 + nn) - (num + 1);for(int i = 1; i <= n; i++) {a[i] = lower_bound(num + 1, num + 1 + nn, a[i]) - num;}for(int i = 1; i <= m; i++) {if(q[i].tp == 1) q[i].x = lower_bound(num + 1, num + 1 + nn, q[i].x) - num;}
}void add_modify(cint p, cint v) {if(p > n || p < 1) return;if(v == pre[p]) return;op[++opCnt] = {1, p, p, pre[p], -1, 0};op[++opCnt] = {1, p, p, pre[p] = v, 1, 0};
}void add_query(cint l, cint r, cint id) {op[++opCnt] = {0, l, r, l - 1, 1, id};
}namespace odt {set<Node> pos[2 * N], tr;int get_pre(cint x) {if(x > n || x < 1) return -1;set<Node>::iterator it = --tr.upper_bound({x, 0, 0});if(x > it->l) return x - 1;it = --pos[it->c].find(*it);return it->r;}int get_suc(cint x) {if(x > n || x < 1) return -1;set<Node>::iterator it = --tr.upper_bound({x, 0, 0});if(x < it->r) return x + 1;it = ++pos[it->c].find(*it);return it->l;}void build() {for(int i = 1; i <= nn; i++) pos[i].insert({0, 0, i});for(int i = 1; i <= nn; i++) pos[i].insert({n + 1, n + 1, i});tr.insert({0, 0, 0});tr.insert({n + 1, n + 1, 0});for(int i = 2, j = 1; i <= n + 1; i++) {if(a[i] != a[j]) {tr.insert({j, i - 1, a[i - 1]});pos[a[i - 1]].insert({j, i - 1, a[i - 1]});j = i;}}}set<Node>::iterator split(cint x) {set<Node>::iterator it = --tr.upper_bound({x, 0, 0});if(it->l == x) return it;int l = it->l, r = it->r, c = it->c;pos[c].erase(*it);tr.erase(it);pos[c].insert({l, x - 1, c});pos[c].insert({x, r, c});tr.insert({l, x - 1, c});tr.insert({x, r, c});return tr.find({x, r, c});}void assign(cint l, cint r, cint c) {set<Node>::iterator itr = split(r + 1), itl = split(l);for(set<Node>::iterator it = itl; ++it != itr; ) {add_modify(it->l, it->l - 1);}vector<int> suc;for(set<Node>::iterator it = itl; it != itr; ++it) {int tmp = get_suc(it->r);if(tmp > r) suc.push_back(tmp);}for(set<Node>::iterator it = itl; it != itr; ++it) {pos[it->c].erase(*it);}pos[c].insert({l, r, c});tr.erase(itl, itr);tr.insert({l, r, c});for(int tmp : suc) {add_modify(tmp, get_pre(tmp));}add_modify(l, get_pre(l));add_modify(get_suc(r), r);}}namespace BIT {int sum[N];inline int lowbit(cint x) { return x & -x; }inline void modify(cint p, cint v) {for(int i = p; i <= n; i += lowbit(i)) sum[i] += v;}inline int query(cint l, cint r) {int res = 0;for(int i = r; i > 0; i -= lowbit(i)) res += sum[i];for(int i = l - 1; i > 0; i -= lowbit(i)) res -= sum[i];return res;}inline void clear(cint p) {for(int i = p; i <= n; i += lowbit(i)) sum[i] = 0;}
};void cdq(int l, int r, int ql, int qr) {if(ql > qr) return;if(l == r) {for(int i = ql; i <= qr; i++) {Op &o = op[swp[i]];if(o.tp == 1) BIT::modify(o.l, o.w);else ans[o.id] += BIT::query(o.l, o.r);}for(int i = ql; i <= qr; i++) {Op &o = op[swp[i]];if(o.tp == 1) BIT::clear(o.l);}return;}int mid = (l + r) >> 1;for(int i = ql; i <= qr; i++) {Op &o = op[swp[i]];if(o.tp == 1 && o.v <= mid) BIT::modify(o.l, o.w);if(o.tp == 0 && o.v > mid) ans[o.id] += BIT::query(o.l, o.r);}for(int i = ql; i <= qr; i++) {Op &o = op[swp[i]];if(o.tp == 1 && o.v <= mid) BIT::clear(o.l);}int lcnt = 0;for(int i = ql; i <= qr; i++) {Op &o = op[swp[i]];if(o.v <= mid) tmp[++lcnt] = swp[i];}int rcnt = lcnt;for(int i = ql; i <= qr; i++) {Op &o = op[swp[i]];if(o.v > mid) tmp[++rcnt] = swp[i];}for(int i = ql; i <= qr; i++) {swp[i] = tmp[i - ql + 1];}// if(rcnt != qr - ql + 1) throw -1;cdq(l, mid,     ql, ql + lcnt - 1);cdq(mid + 1, r, ql + lcnt, qr);
}int main() {cin >> n >> m;for(int i = 1; i <= n; i++) {cin >> a[i];num[++nn] = a[i];}for(int i = 1; i <= m; i++) {int op;cin >> op;if(op == 1) {q[i].tp = 1;cin >> q[i].l >> q[i].r >> q[i].x;num[++nn] = q[i].x;} else {q[i].tp = 0;cin >> q[i].l >> q[i].r;}}lisanhua();odt::build();for(int i = 1; i <= n; i++) {pre[i] = odt::get_pre(i);op[++opCnt] = {1, i, i, pre[i], 1, 0};}for(int i = 1; i <= m; i++) {if(q[i].tp == 0) {add_query(q[i].l, q[i].r, i);} else {odt::assign(q[i].l, q[i].r, q[i].x);}}for(int i = 1; i <= opCnt; i++) swp[i] = i;cdq(0, n - 1, 1, opCnt);for(int i = 1; i <= m; i++) {if(q[i].tp == 0) cout << ans[i] << '\n';}return 0;
}/*20 0
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
1 9 2 3 3 3 6 6 2 2 2 4 4 1 1 9 8 7 6 6
1 1 5 3
3 3 3 3 3 3 6 6 2 2 2 4 4 1 1 9 8 7 6 6
2 5 10
1 2 4 6
3 6 6 6 3 3 6 6 2 2 2 4 4 1 1 9 8 7 6 6
2 1 20
2 1 19
2 1 1820 6
1 9 2 3 3 3 6 6 2 2 2 4 4 1 1 9 8 7 6 6
1 1 5 3
2 5 10
1 2 4 6
2 1 20
2 1 19
2 1 1820 3
1 9 2 3 3 3 6 6 2 2 2 4 4 1 1 9 8 7 6 6
1 1 5 3
1 2 4 6
2 1 20*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/887185.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

任务管理智能化:重塑工作方式的必备工具

智能任务管理工具是一类能够自动化处理任务分配、进度跟踪、团队协作等任务管理活动的软件。这类工具通常利用先进的算法和人工智能技术,帮助用户更高效地完成项目管理、日常任务规划等工作。以下是一些知名的智能任务管理工具及其特点: 一、板栗看板 ● 简介:一款智能驱动的…

猿大师播放器:智慧安防网页播放RTSP H.265实时监控革命,无需转码终极解决方案

一、智慧安防的致命瓶颈:当传统技术拖累生命安全在智慧城市、工业安全、应急指挥等场景中,实时视频监控是守护生命与财产的核心防线。然而,传统RTSP播放方案在延迟、成本、稳定性上的缺陷,正在成为安防系统的“阿喀琉斯之踵”。1. 服务器转码方案:实时监控的“致命延迟”3…

WSL2走主机IP地址代理的方式

前言 工作需求导致需要使用到WSL2,git的时候出现网络原因无法拉取。故记录一下走完整个WSL2代理的流程 WSL1 和 WSL2 网络的区别 在 WSL1 时代,由于 Linux 子系统和 Windows 共享了网络端口,所以访问 Windows 的代理非常简单。例如 Windows 的代理客户端监听了 8000 端口,那…

「渲染101」开启 UE 渲染的高效之门

在 UE 项目制作中,渲染往往耗时费力。云渲染技术的出现带来转机,其中渲染 101 凭借诸多优势脱颖而出。性价比高到离谱 成本真的是选择云渲染平台的关键!3090 显卡原价 7 元 / 小时,充值优惠后居然能低至 3.5 元 / 小时;4090 显卡原价 11 元 / 小时,最低只要 5.5 元 / 小时…

TidGi 太记 v0.12.0 高效管理工具

软件介绍 TTidGi 是一个基于「太微 TiddlyWiki」的知识管理桌面应用,能够保护隐私内容、高级自动化、自动Git云备份、部署为博客,并且可以通过RESTAPI与Anki等应用连接。旨在帮助用户更好地管理时间、提高工作效率和组织任务。无论是个人使用还是团队协作,TidGi 都能提供灵活…

IDM Internet Download Manager下载神器

软件介绍 Internet Download Manager (简称IDM) 是一款Windows 平台功能强大的多线程下载工具,国外非常受欢迎。支持断点续传,支持嗅探视频音频,接管所有浏览器,具有站点抓取、批量下载队列、计划任务下载,自动识别文件名、静默下载、网盘下载支持等功能。 软件截图下载地…

哪吒2制作幕后揭秘:可可豆动画如何高效协作打造顶级动画?

近期,国产动画电影《哪吒2》引爆市场,成为观众热议的焦点。作为《哪吒之魔童降世》的续作,《哪吒2》不仅延续了前作的精良制作与深刻内涵,更在视觉效果和故事叙事上实现了全面升级。而这一切的背后,离不开制作团队——可可豆动画的高效协作与精益求精的创作态度。可可豆动…

GPU-Z v2.62.00 显卡识别检测工具,用于检测和测试GPU性能,中文汉化版

软件介绍 TechPowerUp GPU-Z中文版是一款知名的显卡识别软件,用于检测和测试GPU性能的专业工具。最新版的GPU-Z提供了关于GPU图形显卡和CPU处理器的详细硬件信息。该软件以单个可执行文件形式提供,绿色便携,无需安装。界面直观简洁,带有启动向导,运行后显示GPU核心频率、传…

项目管理神器:精选工程管理工具大盘点

板栗看板作为一款工程管理软件,在项目管理、任务协同、知识笔记以及个人待办等多个方面展现出其独特的优势。以下是对板栗看板在工程管理方面的详细分析: 一、核心功能 任务可视化管理 ○ 板栗看板采用可视化的看板系统,用户可以将任务以卡片的形式展示在看板上,并轻松拖动…

使用 CloudDM 和飞书流程化管理数据库变更审批

CloudDM 是一个专为团队协同工作打造的数据库数据管控平台。在管控数据库安全变更的过程中,为提高效率,方便用户使用,CloudDM 接入了主流 OA 协同办公系统(包括钉钉、飞书、企业微信),支持实时通知与移动办公,满足广大企业用户的实际需求。 本文将介绍如何使用 CloudDM …

Joker 智能开发平台:低代码开发的革新力量

在软件开发领域,开发效率与灵活性始终是开发者们追求的核心目标。随着技术的迅猛发展,低代码开发平台逐渐成为行业焦点,而 Joker 智能开发平台凭借其卓越的性能和创新的功能,脱颖而出,为开发者们带来了前所未有的开发体验。 一、产品概述 Joker 智能可视化开发平台是一款…

解决Unreal Engine使用third party libwebsockets无法连接问题

背景 使用libwebsockets开发了一个SDK,用于建立和服务器的连接,并就接受服务器的推送消息,使用的版本是4.3.3的tag。UE版本是5.3.2 以动态库的方式接入整体SDK,SDK链接了静态的libwebsockets,在qt demo运行良好,但是在接入Unreal Engine demo的时候出现问题。 使用的IDE为…