news 2026/4/15 14:09:20

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
详细揭秘:如何发明小波矩阵

目录标题

以静态区间第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;}};
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/13 11:31:35

新手必看:VibeVoice-TTS网页推理保姆级上手教程

新手必看&#xff1a;VibeVoice-TTS网页推理保姆级上手教程 你是不是也试过——花半天配环境&#xff0c;结果卡在“ModuleNotFoundError”&#xff1b;点开一个TTS工具&#xff0c;界面全是英文参数&#xff0c;连“语速调慢一点”都找不到按钮&#xff1b;好不容易生成30秒语…

作者头像 李华
网站建设 2026/4/13 18:07:54

Python数据分析可视化:Matplotlib实训

&#x1f4c8; 实训揭秘&#xff1a;用 Matplotlib 画出“会说话”的函数图&#xff01; ❝ 你以为数学公式只会躺在课本里&#xff1f; 不&#xff01;它们也能在屏幕上“跳舞”——只要你会用 Matplotlib&#xff01; 今天咱们来玩点“硬核”的&#xff1a; 看懂一张图&#…

作者头像 李华
网站建设 2026/4/14 8:27:07

ollama部署QwQ-32B教程:从GitHub模型仓库到本地推理服务

ollama部署QwQ-32B教程&#xff1a;从GitHub模型仓库到本地推理服务 1. 为什么选QwQ-32B&#xff1f;不只是又一个大模型 你可能已经试过不少文本生成模型&#xff0c;但QwQ-32B有点不一样。它不是那种“你问什么就答什么”的常规助手&#xff0c;而是真正会“想一想再回答”…

作者头像 李华
网站建设 2026/4/12 2:10:21

Z-Image-Turbo本地运行:数据安全更有保障

Z-Image-Turbo本地运行&#xff1a;数据安全更有保障 在电商设计团队的晨会上&#xff0c;市场总监刚提出“今天下午三点前要完成6套春节主图”&#xff0c;设计师小陈已经打开本地终端&#xff0c;输入一行命令——3秒后&#xff0c;第一张10241024高清图出现在屏幕上&#x…

作者头像 李华
网站建设 2026/4/12 22:10:43

MedGemma X-Ray惊艳效果:动态热力图显示AI关注区域与临床征象对应关系

MedGemma X-Ray惊艳效果&#xff1a;动态热力图显示AI关注区域与临床征象对应关系 1. 看得见的“思考过程”&#xff1a;为什么热力图比文字报告更值得信赖 你有没有过这样的经历&#xff1a;AI说“肺部存在浸润影”&#xff0c;但你盯着X光片反复看了三遍&#xff0c;还是不…

作者头像 李华