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

详细揭秘:如何发明小波矩阵

目录标题

以静态区间第k kk小为例。

首先假装你会归并树。归并树是啥?其实就是对归并排序的过程建树。先建立一个线段树,再自底向上归并,求出每个节点对应的区间[ l , r ] [l,r][l,r]的所有元素构成的有序序列。

归并树可以慢速二维数点。具体地,我们要查区间[ l , r ] [l,r][l,r]有多少个≤ x \le xx的数,可以直接找到每个线段树节点,对节点上的序列二分一下。时间复杂度是O ( log ⁡ 2 n ) \mathcal{O}(\log^2 n)O(log2n)的。要是直接拿这玩意二分求区间第k kk小,可以做到高贵的O ( log ⁡ 2 n log ⁡ V ) \mathcal{O}(\log^2 n\log V)O(log2nlogV)复杂度,这也太菜了。

究其原因是没有利用好归并树能做二维数点。不妨换维,将下标与值域互换,相当于求:值域在[ l , r ] [l,r][l,r]内的从左到右第k kk个数。这就成功把要二分的东西放到了线段树维护的维度上,那么可以把二分换成线段树二分做到O ( log ⁡ n log ⁡ V ) \mathcal{O}(\log n\log V)O(lognlogV)

还能再给力一点吗?注意到换维过后值域是O ( n ) \mathcal{O}(n)O(n)的了,如果能O ( n ) \mathcal{O}(n)O(n)地对同一层处理出来每个数前面有几个数,那么就能再去掉一个log ⁡ n \log nlogn。于是我们开始思考怎么干这个事情。

方便起见把线段树换成01trie \text{01trie}01trie,这样可以省掉一些讨论。现在每一层的元素个数是O ( n ) \mathcal{O}(n)O(n),值域也是O ( n ) \mathcal{O}(n)O(n),我们当然希望能把同一层的所有数都搓到一起。于是不妨将所有左子树扔到最前面,右子树扔到最后面,然后把序列拼起来。大概是这样:

这个大概叫“蝴蝶变换”。方便起见把左子树方向称为红色,右子树方向称为蓝色。

这个树的结构就可以扔掉了,因为我只需要知道一个位置前面有几个蓝色元素,就能直接推出它在蝴蝶变换后会到哪个地方。现在在线段树二分上的过程可以写成这样:

  • 从第一层开始。
  • 计算[ l , r ] [l,r][l,r]之间的红色元素数量。
  • 若个数≥ k \ge kk,则往红色子树走。否则往蓝色子树走。

我们甚至只需要知道元素个数!所以每一层做一个前缀和即可。代码大概长这样:

inlineintkth(intl,intr,intk){intres=0;l--;for(inti=29;~i;i--){intql=b[i][l],qr=b[i][r],c=(r-qr)-(l-ql);if(c>=k)l-=ql,r-=qr;elseres|=1<<i,k-=c,l=p[i]+ql,r=p[i]+qr;}returnres;}

其中b i b_ibi是第i ii层的前缀和,p i p_ipi是第i ii层的红色元素个数。这样我们就做到了O ( log ⁡ V ) \mathcal{O}(\log V)O(logV)查询。

还能再再再给力一点吗?时间复杂度上很难优化了,但是空间O ( n log ⁡ V ) \mathcal{O}(n\log V)O(nlogV)还是不够看。发现我们只要算1 11的个数,可以压个位,空间复杂度做到O ( n log ⁡ V w ) = O ( n ) \mathcal{O}\left(\frac{n\log V}{w}\right)=\mathcal{O}(n)O(wnlogV)=O(n)

然后你就吊打主席树了。恭喜你发明了小波矩阵!

structBitset{intn;vector<pair<ull,int>>b;Bitset(){}Bitset(vector<ull>a){n=(a.size()>>6)+1,b.resize(n);for(inti=0;i<a.size();i++)b[i>>6].first|=a[i]<<(i&63);for(inti=1;i<n;i++)b[i].second=b[i-1].second+__builtin_popcountll(b[i-1].first);}inlineintask(intk){returnb[k>>6].second+__builtin_popcountll(b[k>>6].first&(1ull<<(k&63))-1);}};structWavelet{intn;array<Bitset,30>b;array<int,30>p;Wavelet(vector<int>a){n=a.size();vector<int>t(n);for(inti=29;~i;i--){vector<ull>tmp(n);intx=0,y=0;for(intj=0;j<n;j++)tmp[j]=a[j]>>i&1;for(intj=0;j<n;j++)(a[j]>>i&1?t[y++]:a[x++])=a[j];b[i]=Bitset(tmp),p[i]=x;for(intj=0;j<y;j++)a[x+j]=t[j];}}inlineintkth(intl,intr,intk){intres=0;l--;for(inti=29;~i;i--){intql=b[i].ask(l),qr=b[i].ask(r),c=(r-qr)-(l-ql);if(c>=k)l-=ql,r-=qr;elseres|=1<<i,k-=c,l=p[i]+ql,r=p[i]+qr;}returnres;}};
http://www.cnnetsun.cn/news/855776.html

相关文章:

  • ccmusic-database应用场景:数字音乐馆元数据自动打标、流派归档系统建设
  • Qwen3-4B Instruct-2507详细步骤:GPU显存监控+推理吞吐量压测方法
  • 直播字幕生成可行吗?Fun-ASR流式识别尝试
  • 不开源?不!SeqGPT-560M镜像完全开源可部署:本地GPU环境完整迁移指南
  • Qwen3-32B开源可部署方案:Clawdbot网关+Ollama+PostgreSQL持久化教程
  • 无刷电调中的信号玄学:PWM频率与电机控制的微妙平衡
  • Super Resolution如何快速上手?WebUI界面操作入门必看
  • GLM-4.7-Flash保姆级教学:从GPU检测到服务重启的全故障处理
  • 解决Safari中CSS vh异常的实战案例
  • 技术文档也是产品力!看Heygem如何赢得流量
  • Clawdbot一文详解:Qwen3:32B作为核心模型的AI代理扩展系统开发入门
  • 仿真实践 | 基于Simulink的直流电机抗饱和PI控制策略优化
  • GLM-4-9B-Chat-1M效果展示:上市公司年报(PDF+OCR文本)中财务异常指标自动识别与归因
  • 通义千问3-Embedding降本方案:3GB显存部署,单卡成本省60%
  • 电商商品图文字提取实战:用cv_resnet18_ocr-detection快速实现
  • Clawdbot惊艳效果:Qwen3:32B在汽车维修手册问答中关联故障码、电路图与操作视频
  • 国投智能“数据智能全家桶”重磅发布!打通数据洞察至业务行动的关键链路
  • Local SDXL-Turbo效果展示:长提示词分段输入时的画面渐进式演化过程
  • Top-5结果怎么来的?softmax与topk原理解释
  • QWEN-AUDIO实际作品集:电商商品播报、儿童故事、新闻摘要语音
  • OFA-VE在智能硬件中的应用:边缘设备轻量化部署(Jetson Orin实测)
  • CANFD和CAN的区别详解:适合初学者的通俗解释
  • DeepChat实操手册:医疗健康领域AI问诊原型系统——症状分析+用药提醒+报告生成
  • R语言数据分析:DeepSeek辅助生成统计建模代码与可视化图表
  • Qwen3-Reranker-0.6B实操手册:日志分析定位vLLM服务启动失败常见原因
  • Clawdbot整合Qwen3-32B部署案例:Ollama代理+8080→18789网关配置详解
  • 前后端分离医疗挂号管理系统系统|SpringBoot+Vue+MyBatis+MySQL完整源码+部署教程
  • AcousticSense AI惊艳案例:10秒音频片段在16类中最高置信度达98.7%
  • 前后端分离善筹网(众筹)前后台实现设计系统|SpringBoot+Vue+MyBatis+MySQL完整源码+部署教程
  • Vivado2022.2安装教程:解决常见安装错误的实战案例