0%

CF-1072-E. Exquisite Array

题目大意

给定一个长度为 n 的排列 p。定义一个数组为 k-exquisite 当且仅当:

  • 该数组至少包含两个元素

  • 任意两个相邻元素的差的绝对值至少为 k

对于每个 k(从 1 到 n-1),需要求出排列 p 中有多少个子数组是 k-exquisite 的。

输入格式:

  • 第一行:测试用例数 t(1 ≤ t ≤ 25000)

  • 每个测试用例:

    • 第一行:整数 n,排列的长度(2 ≤ n ≤ 10⁵)
    • 第二行:n 个整数 p₁, p₂, …, pₙ,表示排列(1 ≤ pᵢ ≤ n,且各不相同)

输出格式:

对于每个测试用例,输出 n-1 个整数,第 i 个整数表示 i-exquisite 子数组的个数。

数据范围:

  • 时间限制:2 秒

  • 内存限制:256 MB

  • 所有测试用例的 n 之和不超过 2×10⁵

思路讲解

感觉比 D,B 简单。

如果只需要求这个 k-exquisite 子数组的个数,那就直接双指针,不过这波是要求这个 i-exquisite 子数组的个数。

我们想到,其实这个无非就是把整个数组切成了几段,我们可以采用做减法的方式,去解决这个问题。具体来讲,设我们所求的这个子数组参数为 kk,一个差值 Δ\DeltakΔk≤\Delta 可以直接跨过去,k>Δk>\Delta 就不可以。

image

不难想到,我们可以对这个差值进行排序,或者一种更方便的实现是使用这个 map,并在 set 中存放分隔点,初始设置为这个 0,N(其实这里是一个坑点,不要选成 0,N+1 了,因为我们的分隔点采用前向点,最大为 N-1,因此哨兵应该选 N)。不难发现,设我们的新分隔点为 pospos,set 数组中第一个比我们小的数字是 lowlow,第一个比我们大的数字是 highhigh,我们所需要减去的值就是 (poslow)×(highpos)(pos-low)\times(high-pos),即 low 区有 (poslow)(pos-low) 种选择,high 区是 (highpos)(high-pos) 种选择,使用乘法原理乘起来即可。

1
2
3
4
5
6
7
8
9
10
11
set<ll> st={0,N};
for (int i=1;i<=N-1;++i) {
cout<<ans<<" ";
for (auto pos:mp_diff_pos[i]) {
ll low=get_lower(pos);
ll high=*st.lower_bound(pos);
ll minus=(pos-low)*(high-pos);
ans-=minus;
st.insert(pos);
}
}

AC代码

AC
https://codeforces.com/contest/2184/submission/357897043

心路历程(WA,TLE,MLE……)