经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
离线算法 莫队算法进阶
来源:cnblogs  作者:DrRatio  时间:2024/8/19 15:45:44  对本文有异议

算是把之前的坑填一填吧。

这篇文章主要包含带修莫队,二维莫队等莫队算法的进阶应用,观看前请确保您已经熟练掌握了基本的莫队算法,不会的可以戳这里


带修莫队

众所周知,普通莫队是不支持修改的,因为我们为了得到更优的时间复杂度,需要将每次询问离线下来,打乱顺序。

不过我们也可以通过加上一维时间维强行让它支持修改,这里的时间维在题目意义上就是需要进行的修改次数。

主要思想

原本我们的转移只有四种,以当前为 \(\left[l,r\right]\) 为例,我们可以 \(\mathcal{O(1)}\) 扩展到:

  • \(\left[l-1,r\right]\)

  • \(\left[l+1,r\right]\)

  • \(\left[l,r-1\right]\)

  • \(\left[l,r+1\right]\)

若添加上一维时间,那么我们需要进行六种转移:

  • \(\left[l-1,r,time\right]\)

  • \(\left[l+1,r,time\right]\)

  • \(\left[l,r-1,time\right]\)

  • \(\left[l,r+1,time\right]\)

  • \(\left[l,r,time-1\right]\)

  • \(\left[l,r,time+1\right]\)

只是多了种转移方式,并不影响我们转移的复杂度。

离线询问排序的原则,以左端点所在块为第一关键字,右端点所在块为第二关键字,时间为第三关键字升序排列。

块长选定与时间复杂度

块长方面,通常取 \(\mathcal{n^{\frac{2}{3}}}\),最终复杂度为 \(\mathcal{O(n^{\frac{5}{3}})}\)

关于最优块长的分析:

image

摘自 OI-wiki。

实现

P1903 数颜色/ 维护队列 为例。

带修莫队板子题,思路同上,这里主要讲一下两个时间维的转移。

思路很简单,当查询到某次询问 \(q\) 时,若当前时间小于该次时间,则进行转移。若某次更改的点恰好在当前区间范围内,则进行更改操作,更改操作可以视为一次删除和一次添加,操作完成后为了方便之后再回退回去,我们交换删除的元素和更新的元素。

操作分离:

  1. for(int i=1;i<=m;i++)
  2. {
  3. char op;int x,y;
  4. cin>>op>>x>>y;
  5. if(op=='Q')
  6. q[++qcnt].id=qcnt,q[qcnt].time=rcnt,
  7. q[qcnt].x=x,q[qcnt].y=y;
  8. else
  9. r[++rcnt].p=x,r[rcnt].x=y;
  10. }

转移部分:

  1. void add(int x)
  2. {
  3. if(!mp[x]) anss++;
  4. mp[x]++;
  5. }
  6. void del(int x)
  7. {
  8. mp[x]--;
  9. if(!mp[x]) anss--;
  10. }
  11. int main()
  12. {
  13. /*code*/
  14. int L=1,R=0,tim=0;
  15. for(int i=1;i<=qcnt;i++)
  16. {// 转移原则:先扩大后缩小
  17. while(L>q[i].x) add(a[--L]);
  18. while(R<q[i].y) add(a[++R]);
  19. while(L<q[i].x) del(a[L++]);
  20. while(R>q[i].y) del(a[R--]);
  21. while(tim<q[i].time)
  22. {
  23. tim++;
  24. if(L<=r[tim].p&&r[tim].p<=R)
  25. add(r[tim].x),del(a[r[tim].p]);
  26. swap(a[r[tim].p],r[rim].x);
  27. }
  28. while(tim>q[i].time)
  29. {
  30. if(L<=r[tim].p&&r[tim].p<=R)
  31. add(r[tim].x),del(a[r[tim].p]);
  32. swap(a[r[tim].p],r[tim].x);
  33. tim--;
  34. }
  35. ans[q[i].id]=anss;
  36. }
  37. }

二维莫队

普通的莫队算法处理的是序列上的问题,如果我们将它扩展成一个平面,使得转移时可以有四个方向选择,就得到了二维莫队。

二维莫队的转移操作每次移动指针要操作一行或一列的数,具体实现方式与普通的一维莫队类似。

块长选定与时间复杂度

image
摘自 OI-wiki。

简而言之,设矩阵大小为 \(S\),询问次数为 \(q\),我们的块长通常选定为 \(\frac{\sqrt S}{q^{\frac{1}{4}}}\),这样算得的结果若小于 1,我们直接为其赋值为 1 即可。

最终,在求解部分的时间复杂度是 \(\mathcal{O(S·q^{\frac{3}{4}})}\),总时间复杂度为 \(\mathcal{O(S·q^{\frac{3}{4}}+q\log q)}\)

实现

例题:给定一个大小为 \(n\times m\) 的矩阵,接着给出 \(q\) 个询问,每次询问一个子矩阵的权值,权值定义为:若一元素在某矩阵出现了 \(k\) 次,那么它的贡献就为 \(k^2\)

结构体定义:

给定一个矩形的左上角坐标和右下角坐标,我们按左上角横坐标、左上角纵坐标、右下角横坐标、右下角纵坐标所属块的顺序升序排列。

  1. struct Que
  2. {
  3. int lx,ly,rx,ry,id;
  4. bool operator<(const Que &A)const
  5. {
  6. if(lx/B!=A.lx/B) return lx<A.lx;
  7. if(ly/B!=A.ly/B) return ly<A.ly;
  8. if(rx/B!=A.rx/B) return rx<A.rx;
  9. return ry<A.ry;
  10. }
  11. }que[N];

转移部分:

对于行与列,我们用不同的函数

  1. int a[N][N];// 原矩阵
  2. int cnt[N];// 某数出现的
  3. void mdfline(int id,int val,int u,int v)
  4. {// 增加/删除某一行
  5. for(int i=u;i<=v;i++)
  6. anss-=1ll*cnt[a[id][i]]*cnt[a[id][i]],
  7. cnt[a[id][i]]+=val,
  8. anss+=1ll*cnt[a[id][i]]*cnt[a[id][i]];
  9. }
  10. void mdfcolumn(int id,int val,int u,int v)
  11. {
  12. for(int i=u;i<=v;i++)
  13. anss-=1ll*cnt[a[i][id]]*cnt[a[i][id]],
  14. cnt[a[i][id]]+=val,
  15. anss+=1ll*cnt[a[i][id]]*cnt[a[i][id]];
  16. }
  17. int main()
  18. {
  19. /*code*/
  20. B=pow(n*m,0.5)/pow(q,0.25);
  21. if(B<1) B=1;
  22. sort(que+1,que+1+q);
  23. int lx=1,ly=1,rx=0,ry=0;
  24. for(int i=1;i<=q;i++)
  25. {
  26. while(lx>que[i].lx) mdfline(--lx,1,ly,ry);
  27. while(rx<que[i].rx) mdfline(++rx,1,ly,ry);
  28. while(ly>que[i].ly) mdfcolumn(--ly,1,lx,rx);
  29. while(ry<que[i].ry) mdfcolumn(++ry,1,lx,rx);
  30. while(lx<que[i].lx) mdfline(lx++,-1,ly,ry);
  31. while(rx>que[i].rx) mdfline(rx--,-1,ly,ry);
  32. while(ly<que[i].ly) mdfcolumn(ly++,-1,lx,rx);
  33. while(ry>que[i].ry) mdfcolumn(ry--,-1,lx,rx);
  34. ans[que[i].id]=anss;
  35. }
  36. for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
  37. return 0;
  38. }

这些多多少少的莫队算法的改良都是根据不同的题型进行针对化的优化而来的,应用较为局限。但是这些优化的思想仍然对我们做题有很大的帮助,我们仍然要努力弄懂每一种算法,万一哪天就用到了呢?

本文代码全部线上手打,有问题请指出,感谢支持。


完结撒花~

原文链接:https://www.cnblogs.com/Ratio-Yinyue1007/p/18365270

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号