2024年码蹄杯本科组决赛 解题报告 | 珂学家


前言

在这里插入图片描述


题解

和初赛相比,题目质量和难度,还有数据强度明显增强。出的真不错,区分度很强。

码蹄杯平台对非c++还是不够友好T_T.


魔法占卜

难度: 白银
题型: 签到

双指针即可

#include<bits/stdc++.h> 

using namespace std;

int main( )
{
    string s;
    string t;
    cin >> s >> t;

    function<bool()> check = [&]() {
        int j = 0;
        for (int i = 0; i < t.length(); i++) {
            while (j < s.length() && t[i] != s[j]) {
                j++;
            }
            if (j == s.length()) {
                return false;
            }
        }
        return true;
    };

    cout << (check() ? "Lucky!" : "QAQ Unlucky!") << endl;
    return 0;
}

B. 魔法链路

难度: 黄金

思路: 拓扑排序


C. 魔法链路2

难度; 钻石
思路; 矩阵幂优化的DP

这种类型的矩阵幂还是第一次见,学习了。


D. 魔法天平

难度: 钻石

思路: 并查集 + 多重背包优化

  • 题外话1 - 卡语言

c++可以借助bitset优化擦边过。

java二进制优化的多重背包被卡TLE,只有单调队列优化版本才行。

  • 题外话2 - 数据弱

赛时好像放过了贪心解法,哭了。

二进制优化的多重背包

#include <bits/stdc++.h>

using namespace std;

class Dsu {  
private:  
    std::vector<int> arr;  
    std::vector<int> gz;  
  
public:  
    Dsu(int n) : arr(n + 1), gz(n + 1, 1) {}  
  
    int find(int x) {  
        if (arr[x] == 0) {  
            return x;
        }  
        return arr[x] = find(arr[x]);
    }  
  
    void merge(int x, int y) {  
        int a = find(x);  
        int b = find(y);  
        if (a != b) {  
            arr[a] = b;  
            gz[b] += gz[a];  
        }  
    }  
  
    int group(int x) {  
        return gz[find(x)];  
    }  
};  
  
int main() {  

    int n, m;  
    cin >> n >> m;  
  
    Dsu dsu(n);  
    for (int i = 0; i < m; ++i) {  
        int u, v;  
        cin >> u >> v;  
        dsu.merge(u, v);  
    }  
  
    std::map<int, int> gp;
    for (int i = 1; i <= n; ++i) {  
        if (dsu.find(i) == i) {
            gp[dsu.group(i)]++;
        }
    }  

    int mz = n / 2;  
    std::vector<bool> dp(mz + 1, false);  
    dp[0] = true;  
  
    for (const auto& [k, cnt] : gp) {  
        int acc = 0;  
        for (int i = 0; acc + (1 << i) <= cnt; ++i) {  
            for (int j = mz; j >= (1 << i) * k; --j) {  
                if (dp[j - (1 << i) * k]) {  
                    dp[j] = true;  
                }  
            }  
            acc += (1 << i);  
        }  
        int left = cnt - acc;  
        if (left > 0) {  
            for (int j = mz; j >= left * k; --j) {  
                if (dp[j - left * k]) {  
                    dp[j] = true;  
                }  
            }  
        }  
    }  
  
    long long ans = 0;  
    for (int i = mz; i >= 0; --i) {  
        if (dp[i]) {  
            ans = static_cast<long long>(i) * (n - i);  
            break;  
        }  
    }  
  
    std::cout << ans << std::endl;  
    return 0;  
}

E. 魔法合并

难度: 星耀

思路:质数筛 + 树状数组

还是卡java,气炸,换c++秒过

#include <bits/stdc++.h>

using namespace std;

struct BIT {
    BIT(int n) : n(n), arr(n + 1) {}
    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;
        }        
    }
    int n;
    vector<int> arr;
};

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

int main() {

    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    vector<int> primes;
    vector<vector<array<int, 2>>> g(101);
    for (int i = 1; i <= 100; i++) {
        int v = i;
        for (int j = 2; j <= v; j++) {
            if (v % j == 0) {
                int c = 0;
                while (v % j == 0) {
                    c++;
                    v /= j;
                }
                g[i].push_back({j, c});
            }
        }
        if (g[i].size() == 1 && g[i][0][1] == 1) primes.push_back(i);
    }

    int n;
    cin >> n;

    vector<BIT> bits(100, BIT(n));

    vector<int> arr(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> arr[i];
        auto &lst = g[arr[i]];
        for (auto &[k, v] : lst) {
            bits[k].update(i, v);
        }
    }

    int64_t mod = (int64_t)1e9 + 7;
    int q;
    cin >> q;
    while (q-- > 0) {
        int op, l, r;
        cin >> op >> l >> r;
        if (op == 1) {
            int64_t res = 1l;
            for (int v: primes) {
                int u = bits[v].query(r) - bits[v].query(l - 1);
                int64_t p = ksm(v, u + 1, mod);
                p = (p - 1 + mod) % mod;
                int64_t inv = ksm(v - 1, mod - 2, mod);
                p = p * inv % mod;
                res = p * res % mod;
            }
            cout << res << '\n';
        } else {
            auto &lst = g[arr[l]];
            for (auto &[k, v] : lst) {
                bits[k].update(l, -v);
            }
            arr[l] = r;
            auto &lst2 = g[arr[l]];
            for (auto &[k, v] : lst2) {
                bits[k].update(l, v);
            }
        }
    }

    return 0;
}

F. 魔法合并2

难度: 星耀

思路: 线段树

这题,好像lc周赛考过一次


G. 魔法徽章

难度: 星耀

思路: 根号分治 + 无向图有向化

#include<bits/stdc++.h> 

using namespace std;

int main( )
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int n, m;
    cin >> n >> m;

    vector<vector<int>> g(n);
    vector<int> deg(n);
    vector<array<int, 2>> es;
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        u--; v--;
        deg[u]++;
        deg[v]++;
        es.push_back({u, v});
    }

    for (auto &e: es) {
        int u = e[0], v = e[1];
        if (deg[u] < deg[v] || (deg[u] == deg[v] && u < v)) {
            g[u].push_back(v);
        } else {
            g[v].push_back(u);
        }
    }

    long long mod = (long long)1e9 + 7;
    long long ans = 0;
    for (int i = 0; i < n; i++) {
        long long d = deg[i];
        if (d >= 2) {
            ans += d * (d - 1) / 2 * (d - 2) / 3 % mod;
            ans %= mod;
        }
    }

    for (auto &e: es) {
        int u = e[0], v = e[1];
        ans += (deg[u] - 1) * (deg[v] - 1) % mod;
        ans %= mod;
    }

    // 转DAG
    vector<bool> vis(n);
    long long tot = 0;
    for (int i = 0; i < n; i++) {
        for (int u: g[i]) vis[u] = true;
        for (int u: g[i]) {
            for (int v: g[u]) {
                if (vis[v]) tot++;
            }
        }
        for (int u: g[i]) vis[u] = false;
    }

    ans = ((ans - tot * 2) % mod + mod) % mod;
    cout << ans << '\n';

    return 0;
}


H. 魔法修路

难度: 星耀

思路; 并查集 + lca


I. 魔法编程

难度: 王者


J. 魔法咒语

难度: 王者


K. 魔法少女小码妹外传之与小码哥的决斗

难度: 黄金

题型: 签到

模拟即可,但是需要加速收敛


写在最后

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值