D. Frets On Fire
题意:
给你一个长度为
n
n
n的数组,有
q
q
q次询问,每次询问包含一个
l
l
l 和
r
r
r,求原数组中每个元素加上
k
k
k
(
k
∈
[
l
,
r
]
)
(k\in[l, r])
(k∈[l,r])能形成多少个不同的数。
如:原数组为 3 1 4 1 5 9时,
F
r
e
t
Fret
Fret 从
0
0
0到
2
2
2产生的
s
i
+
F
r
e
t
s_i + Fret
si+Fret 为:
Fret | 0 | 1 | 2 |
---|---|---|---|
s 1 s_1 s1 | 3 | 4 | 5 |
s 2 s_2 s2 | 1 | 2 | 3 |
s 3 s_3 s3 | 4 | 5 | 6 |
s 4 s_4 s4 | 1 | 2 | 3 |
s 5 s_5 s5 | 5 | 6 | 7 |
s 6 s_6 s6 | 9 | 10 | 11 |
不同的数为 1,2,3,4,5,6,7,9,10,11 共10个。
题目分析:
对于每一个询问的答案很明显与 l l l 和 r r r 无关而与区间的长度 m = r − l + 1 m=r-l+1 m=r−l+1有关。如果把原数组中的数看成区间的起点,区间的长度为 m m m,那么问题就转换成了求多个区间并集的区间长度。将原数组排序,建立新的数组 d d d,其中 d i = s i + 1 − s i d_{i} = s_{i+1} - s_{i} di=si+1−si 。如果 d i d_{i} di小于 m m m说明区间被合并, d i d_{i} di大于 m m m说明区间为独立区间,二分一下求和就好了。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100005;
ll s[maxn];
ll d[maxn];
ll sd[maxn];
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%I64d", &s[i]);
sort(s + 1, s + 1 + n);
for (int i = 1; i < n; i++) d[i] = s[i + 1] - s[i];
sort(d + 1, d + n);
for (int i = 1; i < n; i++) sd[i] += sd[i - 1] + d[i];
int q;
scanf("%d", &q);
while (q--) {
ll l, r;
scanf("%I64d %I64d", &l, &r);
ll m = r - l + 1;
int pos = lower_bound(d + 1, d + n, m) - d - 1;
ll ans = (n - pos)*m + sd[pos];
printf("%I64d ", ans);
}
return 0;
}