莫队算法
来源:互联网 发布:淘宝p图炒作教程 编辑:程序博客网 时间:2024/05/16 19:02
莫队算法
机房的众神犇都在搞这个东西,本SB
也掺和一下下吧。
莫队算法可用于解决一类可离线且在得到区间
先看这样一个问题:
给出n个数字,m次询问,每次询问在区间
在区间
由于没有加和性质,传统的线段树什么的完全派不上用场了呢!
考虑分子,因为
显然
若得知区间
这时,就需要莫队算法来撑腰了,这也是莫队算法优化的精髓。
注意到,每个区间可以抽象成平面中的点,每次转移的花费都相当与从某点到另一点的曼哈顿距离的长度。恩,所以呢?
所以我们花费的便是这些平面中的点联通的曼哈顿距离。平面点的曼哈顿最小生成树!
对!但平面点的曼哈顿最小生成树怎么求呢?枚举两两点连接
神犇曰:分块是个好东西
确实,利用分块,我们可以实现
分块的做法:
取
用pos数组维护端点i在第pos[i]块中,然后就搞呗。
这样搞:
1):排序,以左段点所在的块为第一关键字,以右端点为第二关键字
2):从左往右处理询问(离线)
3):不断调整l,r的位置并同时修改
时间复杂度证明:
右端点移动:
首先我们考虑一个块里面的转移情况
由于一个块里面的询问都按右端点排序
所以我们右端点在一个块里面最多移动n次
有个块,那么同一个块内的右端点移动最多就是
然后考虑从一个块到另一个块导致的右端点变化
最坏情况,右端点由n到1,那么移动n次
有个块
那么从一个块到另一个块的事件只会发生次……
所以这种右端点移动的次数也是次
没有别的事件导致右端点移动了
左端点移动:
同一个块里面,由于左端点都在一个长度为的区间里面
所以在同一块里面移动一次,左端点最多变化
总共有n个询问……
所以同一块里面的移动最多n次
那么同一个块里面的左端点变化最多是的
考虑跨越块
每由第i个块到第i+1个块,左端点最坏加上
总共能加上次
所以跨越块导致的左端点移动是的
综上,分块做法是。
总结
莫队算法在解决离线区间询问几乎是无敌的。
恩,几乎只要能离线,用分块的莫队算法都能取得一个令人满意的的解法。
所以就有很多扩展(解决线段树等数据结构由于需要区间加和性而不能解决的问题),如区间众数,平均数什么的。
恩。棒!
本文出处: http://www.cnblogs.com/hzf-sbit/p/4056874.html
附:
[BZOJ]2038 小Z的袜子 分块 莫队算法
#include <cstdio>#include <cmath>#include <algorithm>using namespace std;const int maxn = 50000 + 500;typedef long long LL;LL gcd(LL a,LL b){ return (b==0)?a:gcd(b,a%b);}int pos[maxn];int col[maxn];int f[maxn];int n,m;struct Query{ int l,r,id; LL a,b; friend bool operator < (const Query &R,const Query &T) { return pos[R.l]<pos[T.l] || (pos[R.l]==pos[T.l] && R.r<T.r); } void modify() { LL k=gcd(a,b); a/=k,b/=k; }}Q[maxn];bool cmp_id(const Query &a,const Query &b){ return a.id<b.id;}void init(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&col[i]); int limit=(int)sqrt((double)n+0.5); for(int i=1;i<=n;++i) pos[i]=(i-1)/limit+1;//左端点分块 for(int i=1;i<=m;++i) { scanf("%d%d",&Q[i].l,&Q[i].r); Q[i].id=i; } sort(Q+1,Q+m+1);}void modify(int p,LL &ans,int add){ ans=ans+2*add*f[col[p]]+1; f[col[p]]+=add;}void solve(){ LL ans=0; int l=1,r=0; for(int i=1;i<=m;++i) { if(r<Q[i].r) { for(r=r+1;r<Q[i].r;++r) modify(r,ans,1); modify(r,ans,1); } if(Q[i].l<l) { for(l=l-1;Q[i].l<l;--l) modify(l,ans,1); modify(l,ans,1); } if(Q[i].r<r) for(;Q[i].r<r;--r) modify(r,ans,-1); if(l<Q[i].l) for(;l<Q[i].l;++l) modify(l,ans,-1); if(Q[i].l==Q[i].r) { Q[i].a=0,Q[i].b=1; continue; } Q[i].a=ans-(Q[i].r-Q[i].l+1),Q[i].b=(LL)(Q[i].r-Q[i].l+1)*(Q[i].r-Q[i].l); Q[i].modify(); } sort(Q+1,Q+m+1,cmp_id); for(int i=1;i<=m;++i) printf("%lld/%lld\n",Q[i].a,Q[i].b);}int main(){ init(); solve(); return 0;}
- 关于分块算法and莫队算法
- {莫队算法}
- bzoj2038莫队算法
- 莫队算法
- hdu4638莫队算法
- 树上莫队算法
- 莫队算法
- 莫队算法小结
- BZOJ3781【莫队算法】
- BZOJ2038【莫队算法】
- 莫队算法小结
- 莫队算法
- 莫队算法模板
- 莫队算法小结
- 莫队算法
- 莫队算法
- 莫队算法笔记
- 莫队算法
- 打印出所有的水仙花数,所谓水仙花数是指一个三位数,其各位数字立方和等于该数本身。
- OpenCV练习第二弹
- String,StringBuffer,StringBuilder的区别
- Android开发者e周报 第6期
- SpringMVC集成Freemarker绝对路径问题
- 莫队算法
- 【错误处理】Spring JPA的错误及其解决方案
- 为什么你的文案不卖货?5大秘籍帮你写出专业文案
- SPOJ 220 后缀数组
- java5
- java 调用 linux shell 脚本,并读取返回值
- BDS
- echarts 柱状图异常-第一个柱超出y轴
- WebRTC教程