题目
给定一个长度为 \(n\) 的序列 \(a_1,a_2,\ldots,a_n\)。请你找出一共有多少个数对 \((i,j)\) 满足 \(a_i \lt i \lt a_j \lt j\ , 1 \leqslant i, j \leqslant n\) 。
例如,长度为 \(5\) 的序列 \([1, 1, 2, 3, 8, 2, 1, 4]\),有 \(3\) 个满足要求的数对:\((2,4)\)、\((2,8)\)、\((3,8)\):
- 数对 \((2,4)\) 有 \(a_2=1\), \(a_4=3\) 满足 \(a_2 \lt 2 \lt a_4 \lt 4\);
- 数对 \((2,8)\) 有 \(a_2 = 1\),\(a_8 = 4\) 满足 \(a_2 \lt 2 \lt a_8 \lt 8\);
- 数对 \((3,8)\) 有 \(a_3=2\),\(a_8 = 4\) 满足 \(a_3 \lt 3 \lt a_8 \lt 8\)。
限制:
- \(1 \leqslant n \leqslant 2 \times 10^6\)
- \(0 \leqslant a_i \leqslant 10^9\)
算法分析
本题难度中等,考察枚举思想与前缀和技巧。
做法1:
从左到右枚举每个元素 \(a_j\),如果 \(a_j < j\),则在前 \(j-1\) 个元素中找有多少个 \(i\) 满足 \(a_i < i < a_j\)
时间复杂度:\(O(n^2)\)
这个做法可以拿到 \(50\) 分
做法2:
遍历序列 \(a\),如果当前元素满足 \(a_j < j\),需要在 \(a_1 \sim a_{j}-1\) 中找满足 \(a_i < i < a_j\) 的数量进行累加。
可以预处理前 \(j\) 个元素中满足 \(a_j < j\) 的个数,用 \(S_j\) 表示,则在 \(a_1 \sim a_{j}-1\) 中找满足 \(a_i < i < a_j\) 的数量可以转换成 \(S_{a_j-1}\),其中 \(a_j-1 \geqslant 1\)。
时间复杂度:\(O(n)\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
using ll = long long;
int main() {
int n;
cin >> n;
vector<int> a(n+1);
rep(i, n) cin >> a[i];
vector<int> s(n+1);
rep(i, n) s[i] = s[i-1]+(a[i]<i);
ll ans = 0;
rep(j, n) {
if (a[j] < j and a[j]-1 >= 1) {
ans += s[a[j]-1];
}
}
cout << ans << '\n';
return 0;
}