NOIP
noip 考前专训
(来自弱省弱校的挣扎)
T1 专训
38 题,在一个小时内写出正解。
第一题: P11186 三目运算
体现出我不会递归的牛逼情况。
照着题目模拟,类似对一段区间进行染色。
因为递归成一颗二叉树形状,故从最深处返回的下标为紧挨下一次递归开始的下标。
点击查看代码
#ifdef ONLINE_JUDGE
#else
#define FRZ_29
#endif
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
typedef long long LL;
using namespace std;
void RD() {}
template<typename T, typename... U> void RD(T &x, U&... arg) {
x = 0; int f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
x *= f; RD(arg...);
}
void WT() {}
template<typename T> void WT(T &x) {
if (x < 0) { putchar('-'); x = -x; };
if (x > 9) WT(x / 10);
putchar(x % 10 + '0');
}
const int N = 2e6 + 5;
#define PRINT(x) cout << #x << " = " << x << "\n"
#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
#define RF(i, __r, __l) for (int i = __r; i >= __l; i--)
int m, q, ans[N / 10];
char s[N];
int gi(int pos, int &x) {
x = 0;
while (s[pos] >= '0' && s[pos] <= '9') {
x = x * 10 + (s[pos] ^ '0'); pos++;
}
return pos;
}
int dfs(int pos, int l, int r) {
if (s[pos] >= '0' && s[pos] <= '9') {
int t;
int num = gi(pos, t);
LF(i, l, r) ans[i] = t;
return num;
}
int s1;
int s2 = gi(pos + 2, s1);
if (s[pos + 1] == '>') {
int nx = dfs(s2 + 1, s1 + 1, r);
return dfs(nx + 1, l, s1);
} else {
int nx = dfs(s2 + 1, l, s1 - 1);
return dfs(nx + 1, s1, r);
}
}
int main()
{
#ifdef FRZ_29
freopen("z_example.in", "r", stdin);
freopen("z_example.out", "w", stdout);
#endif
RD(m, q); cin >> s;
dfs(0, 1, m + 1);
while (q--) {
int x; RD(x);
printf("%d\n", ans[min(x, m + 1)]);
}
return 0;
}
// START:2024/11/01 21:25:21;
耗时约 45min,体现出我不会递归的牛逼状况。
第二题: P11187 配对序列
简单动态规划。
首先写了 \(O(n^2)\) 的暴力验证想法,像维护最长上升子序列那样。
设 \(f_{i, 0}\) 表示以 \(i\) 为结尾的子序列结尾两字符相等的最长长度,\(f_{i, 1}\) 表示以 \(i\) 结尾的子序列结尾两字符不相等的最长长度。
显然:
\[f_{i, 0} = \max_{j \in [1, i), a_i = a_j}(f_{j, 1} + 1, f_{i, 0})
\]
\[f_{i, 1} = \max_{j \in [1, j), a_i \not= a_j}(f_{j, 0} + 1, f_{i, 1})
\]
\(f_{i, 0}\) 的转移可以记录 \(a_i\) 上一次出现的位置,因为决策区间单调。
\(f_{i, 1}\) 的转移可以记录前缀最大值和次大值,因为要避免重复。
点击查看代码
#ifdef ONLINE_JUDGE
#else
#define FRZ_29
#endif
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
typedef long long LL;
using namespace std;
void read() {}
template<typename T, typename... U> void read(T &x, U&... arg) {
x = 0; int f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
x *= f; read(arg...);
}
void write() {}
template<typename T> void write(T x) {
if (x < 0) { putchar('-'); x = -x; };
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
const int N = 5e5 + 5;
#define PRINT(x) cout << #x << " = " << x << "\n"
#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
#define RF(i, __r, __l) for (int i = __r; i >= __l; i--)
int n, a[N], lst[N];
int f[N][2];
int main() {
#ifdef FRZ_29
freopen("z_example.in", "r", stdin);
freopen("z_example.out", "w", stdout);
#endif
read(n);
LF(i, 1, n) read(a[i]);
LF(i, 1, n) f[i][1] = 1;
pair<int, int> Max[2] = {{0, 0}, {0, 0}};
LF(i, 1, n) {
if (Max[0].second == a[i]) f[i][1] = max(f[i][1], f[i][1] + Max[1].first);
else f[i][1] = max(f[i][1], f[i][1] + Max[0].first);
if (!lst[a[i]]) { lst[a[i]] = i; continue; }
f[i][0] = max(f[i][0], f[lst[a[i]]][1] + 1);
lst[a[i]] = i;
if (f[i][0] > Max[0].first) {
if (Max[0].second == a[i]) { Max[0].first = f[i][0]; continue; }
else {
Max[1] = Max[0];
Max[0] = { f[i][0], a[i] };
}
} else if (f[i][0] > Max[1].first){
if (Max[0].second == a[i]) continue;
Max[1] = { f[i][0], a[i] };
}
}
int ans = 0;
LF(i, 1, n) ans = max(ans, f[i][0]);
// LF(i, 1, n) printf("f[%d]: 0 %d 1 %d\n", i, f[i][0], f[i][1]);
write(ans);
return 0;
}
// START:2024/11/02 13:04:05;
耗时 30min,还行。
第七题:P11021 「LAOI-6」区间测速
体现出我不会用 STL 进行模拟的牛逼情况。
容易发现答案是 \(\max(ans, \left\lfloor\dfrac{|s_i - s_{i + 1}|}{|t_i - t_{i + 1}|}\right\rfloor)\)。
因为要支持改点的操作,所以可以用 set
来维护。
同时改一个点可能会影响 5 个点,也可以用 set
维护。
然后写了 30min 后,错误百出,发现我根本不会 STL。
于是心态崩塌,回去快乐whk。
在冷静一下午后,决定回机房重新写。
写完后 Linux 提醒我有指针越界,然而我很小心的处理了边界情况。
到晚自习时间,无奈放弃。
在回家后,借助 vscode 的强大调试系统,发现我在处理答案时会删去 set
中不存在的元素,改为 multiset
后解决。
体现出我不会 STL 的牛逼情况。
点击查看代码
#ifdef ONLINE_JUDGE
#else
#define FRZ_29
#endif
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#include <map>
#include <set>
using namespace std;
void read() {}
template<typename T, typename... U> void read(T &x, U &...arg) {
x = 0; int f = 1; char ch = getchar();
while (ch > '9' || ch < '0') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= f; read(arg...);
}
void write() {}
template<typename T> void write(T x) {
if (x < 0) { putchar('-'); x = -x; }
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
const int N = 1e5 + 5;
#define PII pair<int, int>
#define PRINT(x) cout << #x << " = " << x << "\n"
#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
#define RF(i, __r, __l) for (int i = __r; i >= __l; i--)
multiset<int> ans;
set<PII> s;
PII a[N];
int n, m, x[N], t[N];
int calc(PII a, PII b) {
return abs(a.second - b.second) / abs(a.first - b.first);
}
int main() {
#ifdef FRZ_29
freopen("z_example.in", "r", stdin);
freopen("z_example.out", "w", stdout);
#endif
read(n, m);
LF(i, 1, n) {
read(x[i], t[i]);
a[i] = {t[i], x[i]};
s.insert({t[i], x[i]});
}
sort(a + 1, a + n + 1);
LF(i, 1, n - 1) ans.insert(calc(a[i], a[i + 1]));
LF(i, 1, m) {
int u, v;
read(u, v);
auto it = s.lower_bound({t[u], x[u]}), l = it, r = it;
int a1 = -1, a2 = -1, a3 = -1;
if (it != s.begin()) {
l--;
a1 = calc(*l, *it);
}
if (it != s.end()) {
r++;
a2 = calc(*r, *it);
}
if (a1 != -1 && a2 != -1) {
a3 = calc(*l, *r);
}
if (a1 != -1) ans.erase(ans.find(a1));
if (a2 != -1) ans.erase(ans.find(a2));
if (a3 != -1) ans.insert(a3);
s.erase(it); s.insert({v, x[u]});
it = s.lower_bound({v, x[u]});
l = it; r = it;
int b1 = -1, b2 = -1;
if (it != s.begin()) {
l--;
b1 = calc(*l, *it);
ans.insert(b1);
}
if (it != --s.end()) {
r++;
b2 = calc(*it, *r);
ans.insert(b2);
}
write(*ans.rbegin());
puts("");
if (a1 != -1) ans.insert(a1);
if (a2 != -1) ans.insert(a2);
if (a3 != -1) ans.erase(ans.find(a3));
if (b1 != -1) ans.erase(ans.find(b1));
if (b2 != -1) ans.erase(ans.find(b2));
s.erase(it);
s.insert({t[u], x[u]});
}
return 0;
}
// START AT 2024 / 11 / 07 17 : 42 : 37
本题总结
- 心态要好。
- 使用 STL 时要尽可能细致些。
- 反复使用的算式应该使用函数来减少错误。
- 体现了我不会 STL 的牛逼情况。