BISHI89 山峰数组计数¶
中等通过率 25.54%python3样例通过牛客 AC
讲解章节:二分
一句话
把正整数数组切成三段,数有多少组切点 (i, j) 使 b1 < b2 > b3。
解题思路¶
这题考什么¶
前缀和 + 单调性 + 二分计数。设 S 为前缀和(S[0]=0),则
两个条件移项后都变成对 S[i] 的上界:
因为 P_i >= 1,S 是严格递增的,所以「S[i] < 某个阈值」的 i 恰好是 一段前缀。固定 j,合法的 i 个数 = min(两个阈值各自的前缀长度, j-1)。 用 bisect 在 S 上二分即可,总复杂度 O(n log n)。 前缀和见 42-前缀和与差分,二分见 44-二分。
(注意 2*S[i] < S[j] 这个条件本身就蕴含 i < j,所以它不需要额外截断; 但第二个条件不蕴含,必须再和 j-1 取 min。)
数据规模与复杂度¶
n <= 2e5。暴力枚举 (i, j) 是 2e10 必 TLE;本做法 2e5 * 17 ≈ 3.4e6。 (其实两个阈值都随 j 单调递增,可以做成双指针的 O(n), 但 bisect 是 C 实现,写起来更短且常数极小,这里用二分。)
坑在哪¶
- 「2*S[i] < S[j]」化成「S[i] < (S[j]+1)//2」才是等价的整数形式: S[i] < S[j]/2 <=> S[i] <= ceil(S[j]/2) - 1 <=> S[i] < (S[j]+1)//2。 直接写 S[j]//2 在 S[j] 为奇数时会少算一个;
- j 的范围是 2 <= j <= n-1(三段都非空,i >= 1 且 j < n);
- 答案可达 C(2e5, 2) ≈ 2e10,C++ 要 long long;Python 无忧;
- bisect 要在「S[1..n]」这个严格递增列表上做,返回值直接就是满足条件的 i 的个数(因为 i 从 1 开始编号)。
样例复核¶
P = [1,2,3,4,5],S(1..5) = [1,3,6,10,15]。 j=2: 阈值1 = (3+1)//2 = 2 -> 1 个;阈值2 = 6-15 = -9 -> 0 个;min = 0。 j=3: 阈值1 = 3 -> 1 个;阈值2 = 12-15 = -3 -> 0 个;min = 0。 j=4: 阈值1 = 5 -> 2 个;阈值2 = 20-15 = 5 -> 2 个;min(2,2,3) = 2。 合计 2,与样例一致。
参考实现¶
[:octicons-arrow-left-16: BISHI88](BISHI88.md) [BISHI90 :octicons-arrow-right-16:](BISHI90.md)