P1102题解报告
趁着题解补充计划,我要好好写题解,加社供。
这是一个五年级蒟蒻的第 12 篇题解,我将用 3 种方法 AC 此题,先放一个题目传送门。
首先,我们发现题目没有让我们求具体位置,只有组数,并且数组之间的数是互相独立的,所以考虑排序后处理。
我们可以将 \(A - B = C\),变为 \(A - C = B\),所以可以枚举每个 \(A\),求出符合条件的 \(B\) 的个数即可。
F1-map
我们可以用一个 map<int, int> 标记 \(a\) 中每个元素在 \(a\) 中出现的次数,然后枚举 \(a\) 中的每个元素,找到 \(A - C\) 在 \(a\) 中出现的次数,累积到答案即可。然后我们发现,枚举 \(A\) 的时候顺序并不重要,所以可以不排序。时间复杂度 \(O(n \log n)\),空间复杂度 \(O(n)\)。
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 5;
int n, c, a[N];
ll ans;
map<int, int> mp;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> c;
for(int i = 1; i <= n; i++){
cin >> a[i];
mp[a[i]]++;
}
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i++) ans += mp.count(a[i] - c) ? mp[a[i] - c] : 0;
cout << ans;
return 0;
}
F2-二分
我们先讲讲 STL 中的两个函数,lower_bound 与 upper_bound。lower_bound(首地址,尾地址,常量) 会用 \(O(\log n)\) 的时间复杂度返回在排好序的首地址和尾地址的左闭右开区间内第一个大于等于常量的地址,要得到它的下标,需要减去数组的地址。而 upper_bound(首地址,尾地址,常量) 会返回在首地址和尾地址的左闭右开区间内第一个大于常量的地址。
我们发现,求 \(x\) 在 \(a\) 中出现的次数,就是用第一个 \(> x\) 的下标减去第一个 \(\ge x\) 的下标,同时加上 \(a\) 的地址后就是他们的地址差,就是 upper_bound(a + 1, a + n + 1, x) - lower_bound(a + 1, a + n + 1, x),那么代码就很好写了,只需统计 \(A - C\) 在 \(a\) 中出现的次数即可。时间复杂度 \(O(n \log n)\),空间复杂度 \(O(n)\)。
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 5;
int n, c, a[N];
ll ans;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> c;
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i++) ans += upper_bound(a + 1, a + n + 1, a[i] - c) - lower_bound(a + 1, a + n + 1, a[i] - c);
cout << ans;
return 0;
}
F3-双指针
其实是三指针。
我们发现,刚才二分的两个东西在数组中是有单调性的,所以我们可以维护两个指针 \(j, k\) 表示第一个 \(> x\) 的下标和第一个 \(\ge x\) 的下标。每次跳到下一个的时候,就修改 \(j, k\)。时间复杂度 \(O(n \log n)\),空间复杂度 \(O(n)\)。
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 5;
int n, c, a[N];
ll ans;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> c;
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + n + 1);
for(int i = 1, j = 1, k = 1; i <= n; i++){
for(; a[j] - a[i] < c && j <= n; j++);
for(; a[k] - a[i] <= c && k <= n; k++);
ans += k - j;
}
cout << ans;
return 0;
}