树状数组
略
求一个数组\(A_1,A_2……A_n\)的逆序对数(树状数组做
\(n ≤ 100000, |A_i| ≤ 10^9\)
我们将\(A_1, ..., A_n\)按照大小关系变成\(1...n\).这样数字的大小范围在\([1, n]\)中.(离散化)
从左往右动态维护一个数组\(B_i\),表示扫描到当前位置有多少个数的大小正好是\(i\)
从左往右扫描每个数,对于\(A_i\),累加\(B_{A_i+1}...B_n\)的和,同时将\(B_{Ai}\)加\(1\).
时间复杂度为\(O(N log N)\).
链接
分治进阶
\(CDQ\)分治
\(CDQ\)分治的思想是用一个子问题来计算对另一个子问题的贡献。
我们注意到在分治的过程中,与\(i\)无关的\([1,i-1]\)和\([i+1,n]\)会由于分割而慢慢地被分离开。对于j对i的贡献,我们在\(j\)与\(i\)被分开的时候去计算.
我们要解决一系列问题,这些问题一般包含修改和查询操作,可以把这些问题排成一个序列,用一个区间\([L,R]\)表示。
分。递归处理左边区间\([L,M]\)和右边区间\([M+1,R]\)的问题。
治。合并两个子问题,同时考虑到\([L,M]\)内的修改对\([M+1,R]\)内的查询产生的影响。即,用左边的子问题帮助解决右边的子问题。
这就是\(CDQ\)分治的基本思想。和普通分治不同的地方在于,普通分治在合并两个子问题的过程中,\([L,M]\)内的问题不会对\([M+1,R]\)内的问题产生影响。
三维\(LIS\)
给定数组\(A, B,\)求最长的子序列,满足\(∀i < j,A_i
\(N ≤ 100000\)
分治+树状数组+\(dp\)
\(f[i]\)表示到\(i\)为止的最长上升子序列,则\(f[i]=max_{j,分治寻找最大的\(j\):
将一个大的区间拆解为两个小区间,先递归处理左区间,然后通过左区间修改右区间,接着再递归处理右区间:
\(1.\)前提是左区间的\(f[i]\)都已经处理好了
\(2.\)我们将左右区间按照\(A[i]\)升序排列
\(3\).那么对于右区间的一个\(A[i]\),我们一定可以找到一个从左区间左侧开始连续的一段区间,这段区间的每一个\(A[j]\)都满足\(A[j];
\(4\).然后将所有满足的\(A[j]\)的相关信息放入一个树状数组中:
树状数组中以\(B[]\)的值为下标(这里假装离散化过),以\(f[]\)的值为树状数组维护的权值
\(5.\)如何求得\(f[i]\):在树状数组中寻找最大的下标在\(1\)~\(B[i]-1\)之间的\(f[j]\),更新\(f[i]\)
\(6.\)不要着急:为了做到树状数组重复使用,记得每次清空树状数组
\(CODE \ from \ fz:\)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
整体二分
区间第K大值
给定长度为\(N\)的序列\(A,Q\)组询问,每组询问给出\(li,ri, ki\),询问第\(li\)个数到第\(ri\)个数之间第\(ki\)大的值是多少。
\(N, Q ≤ 100000, |Ai| ≤ 10^9\)
二分第\(K\)大值为\(mid\),判断区间\([l,r]\)中\(\geq mid\)的数的个数\(ans\),如果\(ans< ki\),则说明答案在\([-∞,mid]\)之间,否则在\([mid,+∞]\)之间。
现在考虑怎么快速计算\(\geq mid\)的个数:
一个显然的想法是我们每次找出大于mid的所有数,然后排序之后通过两次二分找到\([l,r]\)用新数组的下标就可以得到答案了:如下:
然后:???????
分块
Counter
给出一个长度为\(N\)的整数序列\(A\),有\(Q\)组询问,第\(i\)组询问给出\(li,ri\),询问\(li\)到\(ri\)之间有多少个不同的数.
\(N ≤ 100000\)
首先我们对整个序列进行离散化,使得所有的数都落在\(1\)到\(N\)之内。然后对序列分块
我们先预处理第一个信息,令\(F_{i,j}\)表示前\(i\)块里面,数字\(j\)出现的次数.
然后我们考虑一个\(G_{i,j}\),表示第\(i\)块到第\(j\)块里不同数字的个数。那么\(G_{i,j} = G_{i,j−1} + Work(i, j − 1, j).\)
其中\(Work(i, x, y)\)表示的是在第\(i\)块到第\(x\)块的末尾加入第\(y\)块增加的不同数个数。
\(CODE \ from \ fz:\)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
基础莫队
莫队常用于各种区间相关的离线问题,其本质非常的暴力.
我们现在有一个区间\([L, R]\)的答案,并且维护了这个区间的一些信息,现在我们要改求\([L′, R′]\)的答案。
那很简单,只要我们暴力的把\(L\)一步一步地移动到\(L′\),再把\(R\)一步一步移动到\(R′\)就可以辣.
但是如果我们直接暴力总复杂度肯定不对,所以要求离线,用一些分析去把复杂度降低.在之后的讨论里,我们认为序列长度N和询问次数\(Q\)是同阶的.
\(HH\)的项链
给出一个长度为\(N\)的整数序列\(A\),有\(Q\)组询问,第\(i\)组询问给出\(l_i,r_i\),询问\(l_i\)到\(r_i\)之间有多少个不同的数.
\(N ≤ 100000.\)
1.离散化
2.序列维护每种数在\([L, R]\)里面出现了多少次,当左右端点移动时,如果要加入一个数就对应加,否则对应减.当\(cnt\)加\(1\)变成\(1\)时,就把\(Ans\)加\(1\),当\(cnt[x]\)减\(1\)变成\(0\)时,就把\(Ans\)减\(1\).
如何来调整区间访问顺序使得总变化次数较少?
我们将序列按\(\sqrt N\)分块,然后所有的询问先按照左端点所在的块排序,如果左端点所在块相同就按照右端点从小往大排序,这样就可以了.
我们分析移动次数。左端点的移动次数,如果是在同一块内,则单次不超过\(\sqrt N\),否则如果是跨块,由于这样的次数不超过\(\sqrt N\)次,则总复杂度不超过\(N\sqrt N\).
对于右端点,在每一块都会至多花费\(O(N)\)的时间从头扫到尾再从尾扫到头,这个时候总复杂度为\(O(N\sqrt N)\).
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
区间第\(K\)大????
给出一个序列\(A,Q\)次询问,每次询问在\([Li, Ri]\)中第\(K\)大的数是多少.\(N, Q ≤ 100000,\)为了方便,之后的题目中序列数值大小总不超过\(N\).
\(N, Q ≤ 100000.\)
\(gugugu\)
区间\(mex\)
给出一个序列\(A,Q\)次询问,每次询问在\([Li, Ri]\)的\(mex\)是多少.
\(N, Q ≤ 100000.\)
一堆数的\(mex\)指的是不在这些数里出现的最小的自然数.
维护每个数的出现次数,询问的时候从小往大依次判断每个块是否是满的,如果不是满的则暴力扫描.然后从0开始暴力扫描第一个为0的出现次数,即为\([L_i,R_i]\)的\(mex\)
总复杂度为\(O(N\sqrt N).mex\)这个东西是非常有用的.
树上莫队
反 正 我 没 听 懂