给出一个长度为 \(n\) 的数组 \(A\),接下来 \(q\) 次询问,每次询问求 \([l,r]\) 中有多少组 \((i,j,k)\) 使得 \(a_i=a_j=a_k(i 【资料图】 保证 \(1\leq n\leq 10^5,1\leq A_i\leq n(1\leq i\leq n)\)。 简单分析问题,貌似并没有可加性,所以分块和线段树肯定寄了。 但是本题中相邻区间转移是 \(O(1)\): 莫队算法是一种解决此类问题的离线算法,它基于以下思想: 我们不难考虑到一个暴力代码,它的框架如下: 该代码中出现的函数, 不难看出,这份代码本质上是对于询问区间的移动。 如果我们保证区间移动次数较少的话,时间复杂度也会比较优秀。 我们有什么思路呢? 我们取 \(B=\sqrt n\) 为块长进行分块,然后,按照左边界所在的块的编号给询问区间排序,同一个块则用右边界排序。 不难得到如下代码: 我们来分析一下时间复杂度: 因此,莫队的整体时间复杂度是 \(O(n\sqrt n)\)。 对于编号为奇数的块,我们用右边界从小到大排序,对于编号为偶数的块,我们用右边界从大到小排序。 这样会快一点,因为不加优化之前来到新的块右端点需要先回溯至这个块里最小的右端点。 但是加了这个优化,有时我们可以从原先的最右边从右往左依次处理新的块的询问,所以会快一点。void del(LL x){cnt[a[x]]--;sum-=cnt[a[x]]*(cnt[a[x]]-1)/2;}void ins(LL x){sum+=cnt[a[x]]*(cnt[a[x]]-1)/2;cnt[a[x]]++;}LL l=1,r=0,sl,sr;for(int i=1;i<=q;i++){sl=b[i].l,sr=b[i].r;while(l
ins(x)
表示算上 \(x\) 这一项的贡献,del(x)
表示删除 \(x\) 这一项的贡献。bool cmp(node x,node y){if(x.l/B==y.l/B)return x.r
#include