Noip前的大抱佛脚----根号对数算法

根号算法

分块

数列分块入门九题(hzwer)

  • 入门题1,2,3,4,5,7

问题:给一段区间打上标记后单点查询

解法:主要是每块维护一些标记,计算答案等,此类分块较为简单

注意:块大小一般为(sqrt n)

复杂度:(O(nsqrt n))

  • 入门题6

问题:每次朝数列中间插入一个元素,查询第k个元素是什么

解法:块大小超过一定值后暴力重构!采用链表实现

复杂度:(O(nsqrt n))

  • 入门题8

问题:每次询问一个区间内为(c​)的元素个数,并把整个区间改为(c​)

解法:维护一个区间覆盖标记,如果块内没有标记就暴力修改

注意:复杂度分析要用到FlashHu的势能分析

势能(potential energy)是储存于一个系统内的能量,也可以释放或者转化为其他形式的能量。

把一个块搅乱,相当于给其增加(O(sqrt n))的势能,表示其再次被打上全局tag所需要的代价

每次操作最多把两个块搅乱,所以每次操作最多增加(O(2sqrt n))的势能,同时整理好一个块需要的代价是其势能,并能把其势能降为(O(1))

这样子最多搅乱(n)个块,增加(O(2nsqrt n))的势能,最多把他们所有的势能都变成(1),复杂度为(O(4nsqrt n)=O(nsqrt n))

理解:增加势能需要相应代价,减少势能也需要相应代价,对应分析其最大势能即可得出复杂度

拓展:分析每次可以从栈中弹出多个元素的复杂度(还是(O(n)),其总势能最大为(O(n))

  • 入门题9

问题:在线维护区间最小众数

解法:离线就可以用莫队搞了

考虑分块,分块是一个很好的算法,维护每个数在数列中的前缀和是(O(n^2))的时空复杂度,但是给分个块就可以做到(O(nsqrt n))

一段区间的众数一定属于:A.零散块内的数 B.整块内的众数,一共数量不超过(sqrt n)

所以维护两个数组:(f[i][j])表示从第(i)块到第(j)块的众数,这个可以(O(sqrt nsqrt nsqrt n))的预处理出来;(g[i][j])表示离散化后的数字(i)在前(j)块中出现的次数,这个可以(O(n))赋值后(O(nsqrt n))统计前缀和而得到

之后便只需要:A.统计每个数在整块内的出现个数(O(1×sqrt n)) B.统计零散块中的数(O(sqrt n))

综上,复杂度为(O(nsqrt n)),完美通过此题/蒲公英

分块出过什么题

维护序列哈,支持一些在线的区间询问

  • 区间tag单点查询、区间查询等等老掉牙的套路(但是考场上遇到维护序列的题这也是一种思想方式

  • 询问位置(\%p=k)的数的权值和,要求支持单点修改,(valle 1W,nle 15W)(哈希冲突)

对于(plesqrt n),维护(s[p][k])表示答案,这个可以(O(nsqrt n))扫一遍得到答案

对于(p>sqrt n),可以开(s[i][1k])表示第i块的桶,统计前缀和,然后暴力对(sqrt n)的桶扫一遍

综上,复杂度为(O(nsqrt n))

  • 求区间逆序对数,不修改,(nle 5W)(Gty的妹子序列)

先预处理好(s[i][j])表示从第(i)块的开头到(j)位置这一段区间产生的逆序对数,(O(nsqrt n logn))

然后查询时剩下的左半截用主席树暴力查询,(O(nsqrt nlogn))

  • 单点修改,求最短前缀使得前缀(gcd)与前缀(xor)的乘积恰好为(x)(nle 10W)(公约数数列)

有一个奇妙的性质:一个数集的(gcd)在加入一个数后要么不变,要么至少(/2)

对每一块维护一个gcd和、异或前缀和、Map(维护异或前缀和为x的位置)

然后修改就暴力重构该块(O(nsqrt nlogn))

查询就依次扫每一个块,如果这个块没有使得gcd减小,那么这个块内每一个位置的前缀gcd都相同,在Map中查找是否有符合条件的异或和即可;如果gcd减小了就暴力一个一个找,由于上面的性质暴力扫的块不会超过(logn)个。

总复杂度为(O(nsqrt nlogn))

  • 无修改询问区间内数值(\%p=k)的元素个数,(nle 15W)(考试9.20T3)

(plesqrt n),对每块维护(s[i][p][k])表示答案,然后边角暴力统计,(O(nsqrt n))

(p>sqrt n) ,对每块维护(s[i][j])表示桶,然后做一个前缀和暴力查询,(O(nsqrt n))

简直傻逼我做不出真是太菜了

莫队

树莫队

有两种方法,一种是把树划分成若干块,然后暴力去移动其中一个端点;另一种是把树的欧拉序抠出来(像括号序列,每个点出现两次),转化为序列问题后再用序列莫队的方法。我学习的是拓展性更强的后者。两者都要用(vis[i])表示i这个点选了没选,每次(update)就把(vis[x]^=1)

序列莫队

无修

对序列位置分块,每块大小为(sqrt n),将询问离线,按照左端点所在块为第一关键字右端点为第二关键字排序,每次暴力移动转移,复杂度(O(nsqrt n))

带修

块大小为(n^{frac{2}{3}}),按照左端点所在块为第一关键字,右端点所在块为第二关键字,操作时间为第三关键字排序(所有排序都是从小到大),复杂度(O(n^{frac{5}{3}}))

分治

序列分治

一般适用于:一组询问,对象是所有子区间的问题

把区间分治成为跨过中点的区间和左右分治

点分治

  • 树上路径抠成序列后DP(有结合律),可以采用点分治实现(牛客Wannafly24C网址

    实现方式:把询问挂链,分治的时候,把分治区域的(rt)都设置为分治中心,同时给不同子树标个不同的(Tag)(同一子树相同)。每到一个结点便扫一遍询问,当询问的另一个点也在同样的分治区域,并且(Tag)不同,便计算贡献。

    这样能够保证不重不漏地计算所有的贡献,复杂度为(O(nlogn*DP)),为点分治×DP的复杂度(询问只会被扫log次)

倍增

并查集倍增

[SCOI2016]萌萌哒 对区间的每个点一一对应连并查集

原文地址:https://www.cnblogs.com/xzyxzy/p/9903891.html