Description
给定两个长度为 n 的序列 vi,xi,定义一个点对 (i,j)(i≠j)的价值 f(i,j) 为 max(vi,vj)×|xi−xj|,求:
n∑i=1n∑j=1f(i,j)[i≠j]
Solution
很难想象这只是一个绿题,可能拿树状数组做会舒服一点吧,这里介绍分治做法。
我们把 n∑i=1n∑j=1f(i,j)[i≠j] 先拆成 n∑i=1n∑j=1max(vi,vj)[i≠j] 和 n∑i=1n∑j=1|xi−xj|[i≠j],看看分别怎么计算:
- n∑i=1n∑j=1max(vi,vj)[i≠j] 我们把 vi 升序排列一下,那么第 i 个数的贡献即为前 i−1 个数。
- n∑i=1n∑j=1|xi−xj|[i≠j] 我们把 xi 升序排列一下,那么定 j 为右端点,下面这一段就可以如下化简:
j−1∑i=1|xi−xj|=j−1∑i=1xj−xi=j−1∑i=1xj−j−1∑i=1xi=xj×(j−1)−sumj−1
其中 sumi 为前缀和。
那把这两个融合起来怎么搞呢?我们可以用结构体输入这两个序列,以 v 为第一关键字先把序列进行排序。
现在对于区间 [l,r] 计算他的贡献,这一段区间的 v 是已经排好序的,所以考虑分治,将区间分为两半 [l,mid] 和 [mid+1,r],可以通过递归算出贡献在 [l,mid] 的和贡献在 [mid+1,r] 的,接下来要算的就是有贡献的跨越这两个区间的。
我们在 [mid+1,r] 中枚举 j 作为贡献的右界,算 [l,j] 的贡献,并将其加起来作为跨区间的贡献和。将其分拆为 v 和 x 两部分计算:
- v,既然排过序了那么全部都是 vj。
- x,其中 [l,j] 的贡献会有一个位置 i 使得 i 是最后一个 ai(x)≤aj(x) 的,那么区间 [l,j] 的贡献就可以如下计算(sumx[l,r] 为 a[l,r](x) 的和):
xj−xl+xj−xl+1+⋯+xj−xi+xi+1−xj+⋯+xmid−xj=i∑k=lxj−xk+mid∑k=i+1xk−xj=i∑k=lxj−i∑k=lxk+mid∑k=i+1xk−mid∑k=i+1xj=xj×(i−l+1)−sumx[l,i]+sumx[i+1,mid]−xj×(mid−i)
通过预处理前缀和这个是可以 O(1) 求的,也就是对于一个 j,他对答案的贡献为:
vj×(xj×(i−l+1)−sumx[l,i]+sumx[i+1,mid]−xj×(mid−i))
但注意,能将其如上计算的前提是 [l,mid] 和 [mid+1,r] 分别按照 x 进行排序。
我们都知道有归并排序,所以可以处理完上面这些之后再将 [l,r] 以 x 为关键字排一下序。我们不用在处理答案前排序的原因应该很简单,因为有递归函数帮我们处理 [l,mid] 和 [mid+1,r] 的顺序问题,只需要为 [l,r] 外面的区间服务即可。
代码放的是处理贡献的部分。
0 条评论