题意#
给定一个数列 .
定义在有序数对 上的“好对”:对于 , 能使 取最小值。
给定 组询问,每次询问一个区间 中,有多少个好对。
题目求:每次询问的答案 与询问编号 的乘积的和,即:
分析#
关于匹配#
任何一个好对 ,都满足 .
并且通过数据范围可以发现: 都有 ,所以当 固定时,确定 的方法是非常自然的。
将 数组排序,排序后的数组为 ,不妨设 在 中的下标为 ,那么 在 中的下标只可能为 和 .
具体地:
- 如果 ,也就是 距离 更近,那么 在 中的下标为 ;
- 如果 ,此时 距离 最近, 在 中的下标为 ;
- 如果 , 在 中的下标为 或 .
总之,我们可以非常容易地在 复杂度内,对于每个 ,找到能与其构成好的配对的 .
关于询问#
思路#
现在的问题是如何统计答案。
因为题目只要求输出所有询问的和,于是想到将询问离线下来。对于每个好对,统计有多少个询问完全覆盖了它。这个好对对于最终答案的贡献就是:所有覆盖它的询问的编号之和。
对于这个问题,容易想到将所有询问排序,之后用树状数组维护,具体方法:
方法#
从左到右枚举 ,假设枚举到了 .
对于 ,我们已经计算过与其匹配的数,在 中的下标 ,如果 ,那么所有左端点 且右端点 的询问都能包含这个好对。
再考虑答案的形式 ,我们现在要求:左端点 且右端点 的询问的编号和,使用树状数组维护。
于是考虑将询问按照左端点排序,在枚举到 的时候,把所有左端点 的询问加入到树状数组中,把树状数组的位置 的权值加上这个询问的编号 .
这样, 的贡献就等价于:树状数组中 的区间和。
如果与 匹配的 ,在上面的方法中并没有包含到。所以我们再反向枚举一遍 ,方法相同。将右端点 的询问加入树状数组,每次的贡献为树状数组中 的区间和。
代码#
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 3e5 + 10;
const int inf = 0x3f3f3f3f3f3f3f3f;
int c[MAXN];
int n, m;
int lowbit(int x) {
return x & (-x);
}
void update(int x, int k) {
for(int i = x; i <= n; i += lowbit(i)) {
c[i] += k;
}
}
int query(int x) {
int ret = 0;
for(int i = x; i > 0; i -= lowbit(i)) {
ret += c[i];
}
return ret;
}
int query2(int x) {
return query(n) - query(x - 1);
}
int a[MAXN];
struct Node{int num, val;} b[MAXN];
bool cmp(Node A, Node B) {
return A.val <= B.val;
}
struct Query{int val, l, r;} q[MAXN];
bool cmp2(Query A, Query B) {
return A.r >= B.r;
}
bool cmp3(Query A, Query B) {
return A.l <= B.l;
}
int rk[MAXN];
signed main() {
scanf("%lld%lld", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
b[i].val = a[i]; b[i].num = i;
}
sort(b + 1, b + n + 1, cmp);
for(int i = 1; i <= n; i++) {
rk[b[i].num] = i;
}
for(int i = 1; i <= m; i++) {
q[i].val = i;
scanf("%lld%lld", &q[i].l, &q[i].r);
}
sort(q + 1, q + m + 1, cmp2);
int ans = 0;
b[0].val = -inf; b[n + 1].val = inf;
for(int i = n, j = 1; i >= 1; i--) {
while(q[j].r >= i && j <= m) {
update(q[j].l, q[j].val);
j++;
}
//下面六行:判断 t-1 和 t+1,哪个距离 t 更近,能与 t 构成好的匹配。
if(b[rk[i]].val - b[rk[i] - 1].val <= b[rk[i] + 1].val - b[rk[i]].val) {
if(b[rk[i] - 1].num < i) ans += query(b[rk[i] - 1].num);
}
if(b[rk[i]].val - b[rk[i] - 1].val >= b[rk[i] + 1].val - b[rk[i]].val) {
if(b[rk[i] + 1].num < i) ans += query(b[rk[i] + 1].num);
}
}
sort(q + 1, q + m + 1, cmp3);
memset(c, 0, sizeof(c));
for(int i = 1, j = 1; i <= n; i++) {
while(q[j].l <= i && j <= m) {
update(q[j].r, q[j].val);
j++;
}
if(b[rk[i]].val - b[rk[i] - 1].val <= b[rk[i] + 1].val - b[rk[i]].val) {
if(b[rk[i] - 1].num > i) ans += query2(b[rk[i] - 1].num);
}
if(b[rk[i]].val - b[rk[i] - 1].val >= b[rk[i] + 1].val - b[rk[i]].val) {
if(b[rk[i] + 1].num > i) ans += query2(b[rk[i] + 1].num);
}
}
cout << ans << endl;
return 0;
}
cpp