1 条题解
-
0
自动搬运
来自洛谷,原作者为

一只书虫仔
End.搬运于
2025-08-24 22:07:04,当前版本为作者最后更新于2021-03-14 14:03:02,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
Description
给定两个长度为 的序列 ,定义一个点对 ()的价值 为 ,求:
$$\sum\limits_{i=1}^n \sum\limits_{j=1}^n f(i,j)[i \ne j] $$Solution
很难想象这只是一个绿题,可能拿树状数组做会舒服一点吧,这里介绍分治做法。
我们把 $\displaystyle \sum\limits_{i=1}^n \sum\limits_{j=1}^n f(i,j)[i \ne j]$ 先拆成 $\displaystyle \sum\limits_{i=1}^n\sum\limits_{j=1}^n \max(v_i,v_j)[i \ne j]$ 和 $\displaystyle \sum\limits_{i=1}^n \sum\limits_{j=1}^n |x_i-x_j|[i \ne j]$,看看分别怎么计算:
- $\displaystyle \sum\limits_{i=1}^n\sum\limits_{j=1}^n \max(v_i,v_j)[i \ne j]$ 我们把 升序排列一下,那么第 个数的贡献即为前 个数。
- $\displaystyle \sum\limits_{i=1}^n \sum\limits_{j=1}^n |x_i-x_j|[i \ne j]$ 我们把 升序排列一下,那么定 为右端点,下面这一段就可以如下化简:
其中 为前缀和。
那把这两个融合起来怎么搞呢?我们可以用结构体输入这两个序列,以 为第一关键字先把序列进行排序。
现在对于区间 计算他的贡献,这一段区间的 是已经排好序的,所以考虑分治,将区间分为两半 和 ,可以通过递归算出贡献在 的和贡献在 的,接下来要算的就是有贡献的跨越这两个区间的。
我们在 中枚举 作为贡献的右界,算 的贡献,并将其加起来作为跨区间的贡献和。将其分拆为 和 两部分计算:
- ,既然排过序了那么全部都是 。
- ,其中 的贡献会有一个位置 使得 是最后一个 的,那么区间 的贡献就可以如下计算( 为 的和):
通过预处理前缀和这个是可以 求的,也就是对于一个 ,他对答案的贡献为:
$$v_j \times (x_j \times (i-l+1)-sumx_{[l,i]}+sumx_{[i+1,mid]}-x_j \times (mid -i)) $$但注意,能将其如上计算的前提是 和 分别按照 进行排序。
我们都知道有归并排序,所以可以处理完上面这些之后再将 以 为关键字排一下序。我们不用在处理答案前排序的原因应该很简单,因为有递归函数帮我们处理 和 的顺序问题,只需要为 外面的区间服务即可。
代码放的是处理贡献的部分。
Code
int mid = (l + r) / 2; solve(l, mid); solve(mid + 1, r); memset(sum, 0, sizeof(sum)); for (int i = l; i <= r; i++) sum[i] = sum[i - 1] + a[i].x; int i = l - 1; for (int j = mid + 1; j <= r; j++) { while (a[i + 1].x <= a[j].x && i < mid) i++; ans += a[j].v * ((i - l + 1) * a[j].x - sum[i] + sum[mid] - sum[i] - (mid - i) * a[j].x); }
- 1
信息
- ID
- 4048
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者