对于所有数据满足 1<=N<=50,000 1<=M<=50,000 1<=Ai<=10^6 1<=D<=100 1<=U,V<=N
前三个操作都很简单了,LCT就能维护,重点是第四个操作。
求一个区间所有子区间的区间和之和,直接求所有区间和不好求,我们换一种角度去做。
考虑每个点对区间的贡献,假设当前区间是[l,r],对于区间中的点k(l<=k<=r),它的贡献就是它的点权*(k-l+1)*(r-k+1)。
那么我们维护区间答案,考虑怎么上传及修改?
先说上传,就是将一个点的左儿子区间+这个点+这个点的右儿子区间合并。
我们设size[x]为x子树大小,也就是x子树所代表的区间的长度;ls代表左子树,rs代表右子树。
对于左区间,每个点的贡献要加上它从左往右数的排名*它的点权*(1+size[rs])。
对于右区间,每个点的贡献要加上它从右往左数的排名*它的点权*(1+size[ls])。
对于点x要加上它的点权*(1+size[ls])*(1+size[rs])。
发现排名*点权的和无法直接求,因此还要维护两个信息lv[x],rv[x],分别代表x子树所代表区间中每个点点权*从左/从右排名的和。
再看看这两个信息怎么合并,就以lv[x]为例吧,先将左右子节点的lv加上,左子树lv不变,右子树的lv发现每个点排名都加了(1+size[ls]),只要再加上右子树权值和*(1+size[ls])就好了。
综上所述,我们需要维护六个变量val,sum,lv,rv,size,ans,分别代表单点权值、子树权值和、点权*从左数排名之和、点权*从右数排名之和、子树节点数、区间所有子区间和之和即答案。
再说怎么修改?设n代表区间长,v为修改时的增量
val和sum比较常规在这就不说了。
lv和rv都加了
而ans则加了
最后这个推一个通项公式就好了。
知道怎么上传和修改后剩下的就LCT基本操作了。
但要注意翻转时也要把lv和rv交换且旋到原树根的操作splay之后不能只打标记,要先把当前点左右子树翻转,否则当前点的lv和rv是反的。
#include #include