CODEVS3550 不一样的根号算法

根号算法在CODEVS3550题目的应用
博客介绍了如何在OI算法中利用O(sqrt(n))复杂度的根号算法解决CODEVS 3550问题。面对序列的单点修改和极值查询,通过结合线段树与暴力枚举,确定公差d的分界线为K=sqrt(M*N/(M+N)*logN),使得算法复杂度达到O(M*sqrt(M*logN))。在实际实现时,K取值为15以优化常数。

大家都知道在OI算法当中复杂度大都是O(N)、O(N^2)、O(NlogN)......比较常见,而O(sqrt(n))的复杂度并不常见,只有分块、朝鲜树(似乎并不常用但确实复杂度带根号)但是在某些意想不到的地方,可以使用根号算法,不仅效果不错,还比较好写。

引出我们今天的题目CODEVS 3550,已知序列a_1,a_2,...a_i...,a_n,可以进行单点修改,(输入格式为(0,x,y)三元组,意即a_x=a_x+y),可以进行极值查询(输入格式为(1,x,d)三元组,意即查询a_x,a_x+d,a_x+2*d...a_x+k*d中最大值,其中满足x+k*d<=n并且x+(k+1)*d>n(通俗一点来说就是,查询一个首项为x,公差为d的等差数列的数组下标集合所对应的元素极值)

我们所学过的数据结构大多支持连续的区间查询,这样零散的数据怎么办呢。我们可以发现,如果公差d比较小,我们可以对于每种公差d建立d棵线段树,分别保存首项为1,2,......,n-1,n公差为d的下标集合所对应的数;但这种方法在d较大时会导致空间和时间的浪费。如果公差d比较大,我们可以直接暴力枚举。于是很容易想到把两种方法结合起来。那么问题来了,d的分界线在哪呢。

设d的分界限为K,序列长度为N,操作数为K,可以算出时间复杂度为O(N*K*logN(建树)+M*logN(线段树查询)+M*N/K(暴力查询)+M*K*logN(线段树修改)),我们发现这是一个对勾函数,当K=sqrt(M*N/(M+N)*logN),考虑到N与M大致相同,简化为sqrt(M/logN)。这样算法的复杂度变为O(M*sqrt(M*logN))。根号便奇怪的出现在了时间复杂度当中。

在实际实现过程当中,由于线段树常数过大,K应取小一点。

code:(考虑到上面的K太难算了,我取了个常数15= =)

#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define lch t,i<<1
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值