2022.10.22 总结
1. 逐月 P5101
题意
每天都有 \(n\) 头奶牛会在农场中过马路,第 \(i\) 头奶牛会从 \((a_i, 0)\) 走到 \((b_i, 0)\)。
当一头奶牛的行走路线不会和任意一头奶牛的路线相交时,这头奶牛就是 安全的。
请你求出这 \(n\) 头奶牛中,有多少头奶牛是安全的。
思路
58 分
首先,要知道什么样的路线算作相交。
则 \(a_i < a_j, b_i > b_j\) 时,这两条路线是相交的。
所以可以直接暴力枚举两头奶牛的路线,直接判断即可。
时间复杂度
枚举两头奶牛,\(O(n ^ 2)\)。
空间复杂度
数组存储 \(a_i\) 和 \(b_i\),\(O(n)\)。
100 分
在暴力上加上前缀后缀优化即可。
当 \(i\) 是安全的奶牛且已经按照 \(a_i\) 从小到大排序时,\(\max \{b_j\} < b_i, \min \{b_k\} > b_i\) \(j < i, k > i\)
就这样。
时间复杂度
排序,\(O(n \log n)\)。
前缀最大值和后缀最小值,\(O(n)\)。
求答案,\(O(n)\)。
总时间复杂度为 \(O(n)\)。
空间复杂度
数组记录前缀最大值和后缀最小值,\(O(n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, sum1[N], sum2[N], cnt;
struct C{
int a, b;
} a[N];
bool cmp(const C &i, const C &j){
return i.a < j.a;
}
int main(){
freopen("crossings.in", "r", stdin);
freopen("crossings.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].a >> a[i].b;
}
sort(a + 1, a + n + 1, cmp);
sum1[0] = INT_MIN;
for (int i = 1; i <= n; i++) {
sum1[i] = max(sum1[i - 1], a[i].b);
}
sum2[n + 1] = INT_MAX;
for (int i = n; i >= 1; i--) {
sum2[i] = min(sum2[i + 1], a[i].b);
}
for (int i = 1; i <= n; i++) {
cnt += (sum1[i - 1] <= a[i].b && sum2[i + 1] >= a[i].b);
}
cout << cnt;
return 0;
}
2. 逐月 P5082
题意
有一个有 \(w\) 个单词的字典,还有 \(n\) 次查询,每次查询会给出 \(k\) 和前缀 \(i\),也就是要查询有前缀 \(i\) 的按字典序排列的第 \(k\) 个单词,如果没有,输出 \(-1\)。
思路
70 分
先按照字典序排序,排序暴力做 \(n\) 次查询,每次枚举所有的单词,找到满足要求的。
时间复杂度
排序,\(O(w \log w)\)。
查询,\(O(n)\)。
寻找,\(O(w \times 20)\),前缀长度不会超过 \(20\)。
总时间复杂度为 \(O(w \log w + n \times w)\)
空间复杂度
存储 \(w\) 个单词,\(O(w)\)。
100 分
因为每次都是从头开始找单词的,时间复杂度太高了,所以要降低这里。
首先,因为是按照字典序排序,所以有同样前缀的单词在排好序的单词序列中一定是连续的一段。
那么,如果给出的 \(n\) 段前缀也是按照字典序排序的,那么每次就只要从上一次查询到的位置开始找即可。
但是,前缀并不是按照字典序排序的,所以需要把所有的前缀先按字典序排一遍序,再求解。
时间复杂度
排序单词,\(O(w \log w)\)。
排序前缀,\(O(n \log n)\)。
求解,\(O(n \times 20 + w)\)。
空间复杂度
存储 \(w\) 个单词,\(O(w)\)。
存储前缀,\(O(n)\)。
记录答案,\(O(n)\)。
总空间复杂度为 \(O(w + n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int W = 1e6 + 10, N = 1e4 + 10;
int w, n, ans[N];
struct wr{
string s;
int id, k;
} s[W], q[N];
bool cmp(const wr &i, const wr &j){
if (i.s == j.s) {
return i.id < j.id;
}
return i.s < j.s;
}
bool P(const string &i, const string &j){ // 判断前缀
int a = i.size(), b = j.size();
if (a < b) {
return 0;
}
for (int k = 0; k < b; k++) {
if (i[k] != j[k]) {
return 0;
}
}
return 1;
}
int main(){
freopen("auto.in", "r", stdin);
freopen("auto.out", "w", stdout);
cin >> w >> n;
for (int i = 1; i <= w; i++) {
cin >> s[i].s;
s[i].id = i;
}
sort(s + 1, s + w + 1, cmp);
for (int i = 1; i <= n; i++) {
cin >> q[i].k >> q[i].s;
q[i].id = i;
}
sort(q + 1, q + n + 1, cmp);
for (int i = 1, j = 1; i <= n; i++) {
for ( ; j <= w && s[j].s < q[i].s; j++) {
}
int _ = j + q[i].k - 1;
ans[q[i].id] = (_ <= w && P(s[_].s, q[i].s) ? s[_].id : -1); // 记录答案
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << endl;
}
return 0;
}