做法1
以$K$为块大小分块,并对每一个块再维护一个排序后的结果,预处理复杂度为$o(nlog K )$
区间修改时将整块打上标记,散块暴力修改并归并排序,单次复杂度为$o(frac{n}{K}+K)$
区间查询时在整块中二分,散块暴力枚举,单次复杂度为$o(frac{n}{K}log K+K)$
显然取块大小为$K=sqrt{nlog n}$时最优,单次复杂度均为$o(nsqrt{nlog n})$
时间复杂度为$o(nlog n)-o(sqrt{nlog n})$
做法2
瓶颈显然在于整块的二分,考虑对其批量处理
具体的,对于整块的询问先记录在该块上而不执行,在当一个块要被作为散块暴力修改时再处理之前记录的所有操作(标记可以先减去,注意到该块的序列在这次修改前不会变化)
将这些询问基数排序(当底数较大时可以认为是线性的),再利用单调性进行查询,即做到了这样的查询均摊线性(注意$o(K)$的复杂度暴力修改本来就有),那么取$K=sqrt{n}$即可
时间复杂度为$o(nlog n)-o(sqrt{n})$
做法3
实际上是在$frac{n}{K}$个长为$K$的序列中二分,那么即可使用类似[luogu6466]分散层叠算法的做法
具体的,再选择一个阈值$B$,并将连续$B$个块称为一组,对每一组仅考虑每一个块中(排序后)下标是$B$的倍数的位置,即构成$B$个长度为$frac{K}{B}$的序列,对其使用做法4维护
预处理的复杂度即为$o(frac{n}{BK}cdot K)=o(frac{n}{B})$(当然还有$o(nlog n)$的排序复杂度)
区间修改时将整组和整块打上标记,散组和散块都暴力修改,单次复杂度为$o(frac{n}{K}+K)$
区间查询时,将位置分为以下三类:
1.对整组直接查询,至多$o(frac{n}{BK})$组,每组的复杂度为$o(Blog B+log BK)$(可以$o(B+log BK)$找到每一个块中第一个下标是$B$的倍数且大于等于$x^{2}$的数,再向前$B$个位置二分即可)
2.对散组的整块二分查询,至多$o(B)$个块,每块的复杂度为$o(log K)$
3.对散组的散块暴力查询,至多$o(K)$个位置,复杂度也为$o(K)$
综上,单次复杂度为$o(K+frac{n}{K}log B+frac{n}{BK}log BK)$,显然取$K=sqrt{nlog log n},B=log n$最优
时间复杂度为$o(nlog n)-o(sqrt{nloglog n})$
做法4
同样使用[luogu6466]分散层叠算法的做法,但考虑做法4沿用做法3的部分
具体的,直接对$frac{n}{K}$个块建立一个分治结构(也即线段树),并且以$frac{1}{3}$的比例取元素(即合并时仅取下标是3的倍数的位置上的元素),此时序列长度和即为$o(n)$(当然取偶数位置也是$o(n)$的)
区间修改时对整块打上标记(即线段树),并对散块暴力修改后重新维护,注意到一个叶子节点到根路径上的序列长度依次为$K,frac{2}{3}K,frac{4}{9}K,...$,那么总和即为$o(K)$,也即单次复杂度为$o(log frac{n}{K}+K)$
区间查询时对整块直接在该结构上查询,只需要在根上二分一次并递归,注意到一共只有$o(frac{n}{K})$个节点,因此这部分的复杂度为$o(log K+frac{n}{K})$,散块暴力复杂度仍为$o(K)$
综上,单次复杂度为$o(K+frac{n}{K})$,显然取$K=sqrt{n}$最优
时间复杂度为$o(nlog n)-o(sqrt{n})$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 50005 4 #define K 300 5 #define D 3 6 #define ll long long 7 #define L (k<<1) 8 #define R (L+1) 9 #define mid (l+r>>1) 10 int n,k,p,l,r,x,ans,num[11],id[N],bl[N],st[K],ed[K],Pos[2],pos[K<<2][K][2]; 11 ll a[N],tag[K<<2],b[K<<2][K]; 12 int read(){ 13 int x=0,flag=0; 14 char c=getchar(); 15 while ((c<'0')||(c>'9')){ 16 if (c=='-')flag=1; 17 c=getchar(); 18 } 19 while ((c>='0')&&(c<='9')){ 20 x=x*10+(c-'0'); 21 c=getchar(); 22 } 23 if (flag)x=-x; 24 return x; 25 } 26 void write(int x,char c='