EGOI
QOJ 9182
题目描述
在一个环形跑道上,有 \(N\) 名参赛者,分别编号 \(0\) 到 \(N-1\),你的编号为 \(0\)。一开始所有参赛者都在起跑线后,你是其中最靠后的一个。就像这样:
你知道在什么时候你超越了 \(A\),或 \(A\) 超越了你。并且这些超越都不会在起跑线上发生。
没有人往后跑,并且参赛者的速度不恒定。求你至少越过了几次起跑线。
思路
我们记录哪些人还在你前面(你到起跑线之间)。如果你超过了在你前面的人,那么他就会到你后面。但如果你超越了在你后面的人,则说明你越过了一次起跑线,并且所有人都在你前面,除了你超过的人。
时空复杂度 \(O(Q)\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200001;
int n, q, ans, vis[MAXN], tot = 1;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> q;
for(int i = 1, x; i <= q; ++i) {
cin >> x;
if(x < 0) {
vis[-x] = 0;
}else if(vis[x] == tot) {
ans++;
tot++;
vis[x] = tot;
}else {
vis[x] = tot;
}
}
cout << ans;
return 0;
}
QOJ 9183
题目描述
有一排 \(N\) 朵花。如果你摘了第 \(i\) 朵花,那么它左边 \(l_i\) 朵,右边 \(r_i\) 朵花都不能摘。求最多能摘多少朵花。
思路
令 \(dp_i\) 表示最后摘下的花为 \(i\) 时最多能摘多少朵花。
如果 \(j>i+r_i 且 i<j-l_j\),那么 \(dp_j\leftarrow dp_i+1\)。
由于要同时维护两个东西,所以我们一个直接用下标,另一个用树状数组维护。
我们在 \(i+r_i+1\) 的位置插入 \((dp_i,i)\)。这样就满足了第一个条件。
而第二个条件就可以用树状数组记录前缀最大值即可。
空间复杂度 \(O(N)\),时间复杂度 \(O(N\log N)\)。
代码
#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
const int MAXN = 200005;
int n, l[MAXN], r[MAXN], dp[MAXN], tr[MAXN], ans;
vector<pii> ve[MAXN];
int lowbit(int x) {
return x & -x;
}
void update(int p, int x) {
for(; p <= n; tr[p] = max(tr[p], x), p += lowbit(p)) {
}
}
int Getsum(int p) {
int sum = 0;
for(; p; sum = max(sum, tr[p]), p -= lowbit(p)) {
}
return sum;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1, l, r; i <= n; ++i) {
cin >> l >> r;
for(auto [id, val] : ve[i]) {
update(id, val);
}
dp[i] = Getsum(max(0, i - l - 1)) + 1;
ans = max(ans, dp[i]);
if(i + r + 1 <= n) {
ve[i + r + 1].emplace_back(i, dp[i]);
}
}
cout << ans;
return 0;
}