2024年码蹄杯职业院校赛道 国赛 解题报告(流水账版) | 珂学家


前言

在这里插入图片描述


题解

VP了下这个赛道的国赛,感觉还是能AK的。

涉及的知识点,还是蛮多的,但是大部分点到为止。

压轴的题出得不错,但还是偏板子。


小码哥的滞销

难度: 钻石

思路: 反悔贪心

很典的反悔贪心,根据时间排序,然后用高价值替换低价值。

#include<bits/stdc++.h> 

using namespace std;

struct Tx {
    int w, t;
};

int main( )
{
    int n;
    vector<Tx> arr;
    cin >> n;
    for (int i = 0; i < n; i++) {
        int w, t;
        cin >> w >> t;
        arr.push_back({w, t});
    }
    sort(arr.begin(), arr.end(), [&](auto &a, auto &b) {
        return a.t < b.t;
    });

    long long res = 0;
    priority_queue<int, vector<int>, greater<int> > pq;
    for (auto &e: arr) {
        if (e.t > pq.size()) {
            res += e.w;
            pq.push(e.w);
        } else {
            if (!pq.empty() && e.w > pq.top()) {
                res -= pq.top();
                res += e.w;
                pq.pop();
                pq.push(e.w);
            }
        }
    }
    cout << res << endl;

    return 0;
}

小兔子乖乖,把门开开

难度: 钻石

思路: 单调队列+前缀和

关键词: 不超过k个元素的连续子数组,求最优解(最大/最小)

看到这些的时候,往往和单调队列能关联上,事实也是如此。

#include<bits/stdc++.h> 

using namespace std;

int main( )
{
    int n, m;
    cin >> n >> m;

    vector<int> arr(n);
    for (int &x: arr) cin >> x;

    vector<long long> pre(n + 1, 0);
    for (int i = 0; i < n; i++) {
        pre[i + 1] = pre[i] + arr[i];
    }

    long long res = -0x3f3f3f3f;
    deque<int> deq;
    deq.push_back(-1);
    for (int i = 0; i < n; i++) {
        if (!deq.empty() && i - deq.front() > m) {
            deq.pop_front();
        }
        long long cur = pre[i + 1];
        while (!deq.empty() && pre[deq.back() + 1] >= cur) {
            deq.pop_back();
        }
        deq.push_back(i);
        if (deq.size() == 1) {
            res = max(res, (long long)arr[i]);
        } else {
            res = max(res, pre[i + 1] - pre[deq.front() + 1]);
        }
    }
    
    cout << res << endl;
    return 0;
}

出题人的烦恼

难度: 钻石
思路: 并查集 + 拓扑排序找环

图论综合题

  • 增量构造的时候,判断构建环的时机
  • 提取环

确切地讲,就是独立的点,逐渐合并,呈现森林化,直到其中一棵树(也是唯一)出现了环。

#include <bits/stdc++.h>

using namespace std;

class Dsu {
private:
    int n;
    vector<int> arr;
public:
    Dsu(int n): n(n), arr(n + 1, 0) {}
    void merge(int u, int v) {
        int fu = find(u);
        int fv = find(v);
        if (fu != fv) {
            arr[fu] = fv;
        }
    }
    bool same(int u, int v) {
        return find(u) == find(v);
    }    
    int find(int u) {
        if (arr[u] == 0) {
            return u;
        }
        return arr[u] = find(arr[u]);
    }
};

int main() {

    int n, m;
    cin >> n >> m;
    
    vector<int> du(n + 1, 0);
    vector<vector<int> > g(n + 1, vector<int>());
    
    Dsu dsu(n);
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        
        du[u]++;
        du[v]++;
        g[u].push_back(v);
        g[v].push_back(u);
        
        if (!dsu.same(u, v)) {
            dsu.merge(u, v);
        } else {
            break;
        }
    }
    
    // 拓扑排序
    deque<int> que;
    for (int i = 1; i <= n; i++) {
        if (du[i] == 1) {
            que.push_back(i);
        }
    }
    while (!que.empty()) {
        int u = que.front();
        que.pop_front();
        vector<int> &arr = g[u];
        int nv = arr.size();
        for (int i = 0; i < nv; i++) {
            int v = arr[i];
            if (--du[v] == 1) {
                que.push_back(v);
            }
        }
    }
    
    vector<int> res;
    for (int i = n; i >= 1; i--) {
        if (du[i] == 2) {
            res.push_back(i);
        }
    }
    int k = res.size();
    for (int i = 0; i < k; i++) {
        cout << res[i] << " \n"[i == k - 1];
    }
    
    return 0;
}

区间数据处理

难度: 星耀

思路: 离散化 + 树状数组

其实具体的实现还是多种思路

  • 莫队
  • 离线+拆成前后操作序列
#include<bits/stdc++.h> 

using namespace std;

class Fenwick {
public:
    Fenwick(int n): n(n), arr(n + 1, 0) {}
    int query(int p) {
        int r = 0;
        while (p > 0) {
            r += arr[p];
            p -= p & -p;
        }
        return r;
    }
    void update(int p, int d) {
        while (p <= n) {
            arr[p] += d;
            p += p & -p;
        }
    }
private:
    int n;
    vector<int> arr;
};

int main( )
{
    int n, q;
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin >> n >> q;

    set<int> st;
    vector<array<int, 4>> ops;
    for (int i = 1; i <= n; i++) {
        int v;
        cin >> v;
        st.insert(v);
        ops.push_back({0, i, v, 0});
    }
    for (int i = 0; i < q; i++) {
        int l, r, x;
        cin >> l >> r >> x;
        ops.push_back({1, l - 1, i, x});
        ops.push_back({2, r, i, x});
        st.insert(x);
    }

    map<int, int> hp;
    int ptr = 0;
    for (int k: st) {
        hp[k] = ++ptr;
    }
    
    vector<int> res(q, 0);
    Fenwick bit(ptr);
    sort(ops.begin(), ops.end(), [&](auto &a, auto &b) {
        if (a[1] != b[1]) return a[1] < b[1];
        return a[0] < b[0];
    });

    for (auto &op: ops) {
        if (op[0] == 0) {
            bit.update(hp[op[2]], 1);
        } else if (op[0] == 1) {
            res[op[2]] -= (bit.query(ptr) - bit.query(hp[op[3]] - 1));
        } else {
            res[op[2]] += (bit.query(ptr) - bit.query(hp[op[3]] - 1));
        }
    }

    for (auto x: res) {
        cout << x << '\n';
    }

    return 0;
}

破解宝物封印

难度: 钻石
思路: 组合数学 + 逆元 + 费马小定律

很板的一道基础数论题,属于那种会做的人很容易,没学过的人绝对不会。

本质上

C ( n , k ) = n ! / ( k ! ∗ ( n − k ) ! ) C(n, k) = n! / (k! * (n - k)!) C(n,k)=n!/(k!(nk)!)

要么用大数计算,但是大数会非常的慢。

如果在模数前提下,是可以引入逆元的概念,来线性处理。

但是顺便吐槽下,为何模数是 p = 1 0 8 + 7 p = 10^8 + 7 p=108+7

因为一般模数约定为 1 0 9 + 7 或者 998244353 10^9+7或者998244353 109+7或者998244353,我更相信是出题人笔误了,然后将错就错,T_T.

#include<bits/stdc++.h> 

using namespace std;

using int64 = long long;

int64 ksm(int64 b, int64 v, int64 p)
{
    int64 r = 1;
    while (v > 0) {
        if ((v & 1) == 1) {
            r = r * b % p;
        }
        b = b * b % p;
        v /= 2;
    }
    return r;
}

int main()
{
    int64 mod = (int)1e8 + 7;
    
    int n, m;
    cin >> n >> m;

    vector<int64> fac(n + 1, 0);
    vector<int64> inv(n + 1, 0);
    fac[0] = 1;
    for (int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i % mod;
    }
    // 费马小定律
    inv[n] = ksm(fac[n], mod - 2, mod);
    for (int i = n - 1; i >= 0; i--) {
        inv[i] = inv[i + 1] * (i + 1) % mod;
    }

    int64 ans = fac[n] * inv[m] % mod * inv[n - m] % mod;
    cout << ans << endl;

    return 0;
}

数字游戏

难度: 星耀

思路: lazy 线段树

很板的一道题


写在最后

在这里插入图片描述

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值