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
本题总结
  1. 心态要好。
  2. 使用 STL 时要尽可能细致些。
  3. 反复使用的算式应该使用函数来减少错误。
  4. 体现了我不会 STL 的牛逼情况。
posted @ 2024-11-05 13:40  FRZ_29  阅读(7)  评论(0编辑  收藏  举报