莫队算法学习小记

算法创始人

莫涛大神。
莫涛队长的算法,%%%%%%%%%

算法简介

算法前提

可以在 O ( 1 ) O(1) O(1)的时间内把[l,r]的询问转移到[l-1,r],[l+1,r],[l,r-1],[l,r+1]的询问,而且不需要修改操作,那么就可以使用莫队算法([a,b]表示从a到b的区间,包含a和b)

算法核心

假如有一个询问[l,r]要转移到一个询问[l1,r1],那么需要的时间为 O ( ∣ l 1 − l ∣ + ∣ r 1 − r ∣ ) O(|l1-l|+|r1-r|) O(l1l+r1r),在算法前提下,可以用这么多的时间暴力转移。
但是可以发现有时候有些点会被来回算很多次,这样大量浪费了时间,所以莫涛大神就想到了一个方法,把这些询问离线的拍一次序,让有些点可以被算的次数少一些。

l=1;r=0;\\这样初始化可以避免一些不必要的步骤。
fo(i,1,m){
	k=a[i].a;t=a[i].b;
	if(k>l)update(l,k-1,-1);\\update的1表示加,-1表示减,具体操作因题而异。
	else if(k<l)update(k,l-1,1);
	if(t>r)update(r+1,t,1);
	else if(r>t)update(t+1,r,-1);
	l=k;r=t;
}
排序的方法是分块

把序列中的所有点按照 n \sqrt n n 来分块,然后所有询问的左端点所在的块为第一关键字,右端点为第二关键字,然后做一次双关键字排序,即可。

bool cmp(node x,node y){
    return x.d<y.d||x.d==y.d&&x.r<y.r;
}

很多人排序的时候是以右端点所在的块为第二关键字的,但是这样还慢一些。可以看看下面的时间复杂度分析,直接以右端点为第二关键字是最快的(注意是右端点,不是右端点所在的块)

    fo(i,1,m){
	    scanf("%lld%lld",&a[i].a,&a[i].b);
	    a[i].d=(a[i].a-1)/kuai+1;\\分块
	    a[i].c=i;
    }
    sort(a+1,a+1+m,cmp);
创始人的改进

因为发现|l1-l|+|r1-r|是曼哈顿距离,所以把每个的询问看作是二维平面上的一个点,然后构造最小生成树,沿着树边走即可。

一般转移只用暴力即可

很少用大神说的那么复杂的方法,正常的暴力也挺快的。

时间复杂度分析

时间复杂度为 O ( n 1.5 ) O(n^{1.5}) O(n1.5)
有两个角度。
角度一:看看右端点。一个块的r最多到n,每次从上一个块到达下一个块的r复杂度为n,一共有 n \sqrt n n 个块,所以复杂度为 O ( n n ) O({n}\sqrt {n}) O(nn )
角度二:看看左端点。每次左端点从一个块到另一个块的复杂度为 O ( n ) O(\sqrt n) O(n )。在每一块中左指针的移动总量是 O ( Q n ) O(Q\sqrt n) O(Qn ),Q是落在那个块的查询的数量。对于所有的块,总的复杂度为 O ( n n ) O(n\sqrt n) O(nn )
所以总的复杂度为 O ( n n ) = O ( n 1.5 ) O(n\sqrt n)=O(n^{1.5}) O(nn )=O(n1.5)

一些算法的限制

1、如前所述,该算法是离线的,这意味着当我们被强制按照特定的顺序查询时,我们不能再使用它。
2、有时加入删除操作比较困难会带个log,复杂度就会退化为 O ( n log ⁡ n n ) O(n\log n\sqrt n) O(nlognn )。不过有时这样连10^5都能过。
3、如果有待修改的话,就要用到待修改的莫队,不会的参见带修改的莫队算法学习小记
其余的离线题目几乎都可以做。

原始的题目:小Z的袜子(算法的第一道题——入门题)

一道莫队算法的入门题。
小Z的袜子

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值