【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(8)

比赛链接
本文发布于博客园,会跟随补题进度实时更新,若您在其他平台阅读到此文,请前往博客园获取更好的阅读体验。
跳转链接:https://www.cnblogs.com/TianTianChaoFangDe/p/18848680

开题 + 补题情况

这场难度不算很大,但有点吃思维,感觉想出来了的话代码量都不大。
image

1007 - 架子鼓

不难发现,只需要对所有数字乘上 \(q\) 的数据范围的最小公倍数,就可以把所有分数转化为整数了,然后就可以用 \(map\) 来记录其中一个,查询另外一个来得到同时击打的时刻数量了。

点击查看代码
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;

const int N = 2e5 + 9;

i64 lc;

i64 lcm(i64 x, i64 y) {
    return x / std::gcd(x, y) * y;
}

void solve()
{
    int n, m;std::cin >> n >> m;

    std::vector<i64> a(n), b(m);

    for(int i = 0;i < n;i ++) {
        i64 p, q;std::cin >> p >> q;
        p = p * lc / q;
        a[i] = p;
    }

    for(int i = 0;i < m;i ++) {
        i64 p, q;std::cin >> p >> q;
        p = p * lc / q;
        b[i] = p;
    }

    std::map<i64, bool> vis;
  
    i64 sum = 0;
    for(int i = 0;i < n;i ++) {
        vis[sum] = true;
        sum += a[i];
    }

    i64 ans = 0;
    sum = 0;
    for(int i = 0;i < m;i ++) {
        if(vis.count(sum))ans ++;
        sum += b[i];
    }

    std::cout << ans << '\n';
}

/*1,2,3,4,6,8,16*/

int main()
{
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);

    lc = lcm(1, 2);
    lc = lcm(lc, 3);
    lc = lcm(lc, 4);
    lc = lcm(lc, 6);
    lc = lcm(lc, 8);
    lc = lcm(lc, 16);

    int t = 1;std::cin >> t;
    while(t --)solve();

    return 0;
}

1003 - 独到寒山顶

一开始没注意到可以往下走,以为是一个 DP,结果一看可以往下走,并且数据范围乘起来也就 \(250000\),那不就直接 \(BFS\) 最短路嘛。

点击查看代码
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;

const int N = 2e5 + 9;

struct Node {
    int x, y, d;
};

void solve()
{
    int n, m;std::cin >> n >> m;
    std::vector a(n + 2, std::vector<int>(m + 2));
    std::vector vis(n + 2, std::vector<int>(m + 2));

    for(int i = 1;i <= n;i ++) {
        int k;std::cin >> k;
        for(int j = 1;j <= k;j ++) {
            int x;std::cin >> x;
            a[i][x] = true;
        }
    }

    std::queue<Node> q;

    for(int i = 1;i <= n;i ++) {
        if(a[i][1] == 1)continue;
        q.push({i, 1, 1});
    }

    while(q.size()) {
        auto [x, y, d] = q.front();
        q.pop();

        if(vis[x][y])continue;
        vis[x][y] = true;

        if(y == m) {
            std::cout << d << '\n';
            return;
        }

        if(x - 1 >= 1 && a[x - 1][y] != 1) {
            q.push({x - 1, y, d + 1});
        }

        if(x + 1 <= n && a[x + 1][y] != 1) {
            q.push({x + 1, y, d + 1});
        }

        if(y + 1 <= m && a[x][y + 1] != 1) {
            q.push({x, y + 1, d + 1});
        }

        if(y - 1 >= 1 && a[x][y - 1] != 1) {
            q.push({x, y - 1, d + 1});
        }
    }
}

int main()
{
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);

    int t = 1;std::cin >> t;
    while(t --)solve();

    return 0;
}

1001 - 拼图游戏

对于这个题,我们可以考虑 \(O(n \log n)\) 的做法,我们枚举 \(x\) 的大小,由于只要有一个 \(y\) 满足了题意,则更大的 \(y\),也一定满足,因此可以二分最小满足的 \(y\) 值。
至于颜色,我们可以用一个 \(bitset\) 来记录,这样的话,只需要调用 \(bitset\)\(all()\) 函数就可以判断是否所有颜色都出现过了,不过要注意,由于 \(bitset\) 的大小是在编译时就决定了的,不可变的,因此只能直接往最大了开,因此要预先把不合法的颜色设置为 \(1\)

点击查看代码
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;

const int N = 2e5 + 9;

void solve()
{
    int n, m, k;std::cin >> n >> m >> k;
    std::vector a(m, std::vector<int>(n));

    for(int j = 0;j < n;j ++) {
        for(int i = 0;i < m;i ++) {
            std::cin >> a[i][j];
        }
    }

    std::vector<std::bitset<2001>> pre(m);
    for(int i = 0;i < m;i ++) {
        pre[i].set(0, 1);
        for(int j = k + 1;j <= 2000;j ++) {
            pre[i].set(j, 1);
        }
    }

    i64 ans = 0;
    for(int j = 0;j < n;j ++) {
        for(int i = 0;i < m;i ++) {
            pre[i] = pre[i].set(a[i][j], 1);
            if(i - 1 >= 0)pre[i] = pre[i] | pre[i - 1];
        }

        int l = -1, r = m;
        while(l + 1 < r) {
            int mid = l + r >> 1;
            if(pre[mid].all())r = mid;
            else l = mid;
        }

        ans += m - r;
    }

    std::cout << ans << "\n";
}

int main()
{
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);

    int t = 1;std::cin >> t;
    while(t --)solve();

    return 0;
}

1002 - 缓存系统

01 背包板题,由于数据出锅导致众多人最后才做出来。
这里和 01 背包板子题唯一不同就是,对于每一行,有很多个不同的前缀取值,但是只能取一个,因此,转移的时候,不能把行这个维度省略掉,要从上一行转移过来。
除此之外,还不要忘了不选这一行的任何值直接继承上一行的情况(本人因为这个被卡了 \(20\) 分钟)。

点击查看代码
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1
#define int long long

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;

const int N = 2e5 + 9;

void solve()
{
    int n, m, x;std::cin >> n >> m >> x;

    std::vector a(n + 1, std::vector<int>(m + 1)); // 空间
    std::vector b(n + 1, std::vector<int>(m + 1)); // 次数
    std::vector dp(n + 1, std::vector<int>(x + 1));

    int sum = 0;
    for(int i = 1;i <= n;i ++) {
        for(int j = 1;j <= m;j ++) {
            std::cin >> a[i][j] >> b[i][j];
            sum += b[i][j];
            a[i][j] += a[i][j - 1];
            b[i][j] += b[i][j - 1];
        }
    } 

    for(int i = 1;i <= n;i ++) {
        for(int j = 1;j <= m;j ++) {
            for(int k = 0;k <= x;k ++) {
                dp[i][k] = std::max(dp[i][k], dp[i - 1][k]);
            }
            for(int k = x;k - a[i][j] >= 0;k --) {
                dp[i][k] = std::max(dp[i - 1][k - a[i][j]] + b[i][j], dp[i][k]);
            }
        }
    }

    int ans = 0;
    for(int i = 1;i <= n;i ++) {
        for(int j = 0;j <= x;j ++) {
            ans = std::max(ans, dp[i][j]);
        }
    } 

    std::cout << sum - ans << '\n';
}

signed main()
{
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);

    int t = 1;std::cin >> t;
    while(t --)solve();

    return 0;
}

1010 - 字符串哈希(补题)

这个题,还真要用上字符串哈希的一些思考方向才能 get 到出题人想让你干什么。
分别分析一下 \(A(s)\)\(B(s)\) 有什么性质。

  • 由于 \(A(s)\) 是没有取模的,因此每一个 \(A(s)\) 的值,对应唯一的一个字符串。
  • 由于 \(B(s)\)\(10007\) 取模了,所以 \(B(s)\) 的取值范围被限制在了 \([0, 10007)\)

那么,我们可以枚举 \(B(s)\) 的值呀!我们枚举 \(B(s)\) 的值,并且按照计算公式计算出 \(A(s)\) 的值,再用 \(A(s)\) 的值还原出字符串的 \(ord\) 序,判断是否合法(是否超出长度范围或者是否出现了 \(0\) 作为 \(ord\)),如果合法,再按 \(B(s)\) 的规则还原出 \(B(s)\) 的值,判断是否和枚举的 \(B(s)\) 的值相同,如果相同,则答案 \(+1\)

点击查看代码
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1
#define int long long

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;

const int N = 2e5 + 9;
const int M = 10007;

void solve()
{
    int k, c, d, e, f;std::cin >> k >> c >> d >> e >> f;

    std::vector<int> ci(16);

    ci[0] = 1;
    for(int i = 1;i <= 15;i ++) {
        ci[i] = (ci[i - 1] * 10) % M;
    }

    int ans = 0; 
    std::vector<int> tmp;
    for(int i = 0;i < M;i ++) {
        tmp.clear();
        __int128_t now = c * i * i * i + d * i * i + e * i + f;

        while(now) {
            tmp.push_back((int)(now % 27));
            now /= 27;
        }

        if(tmp.size() > k)continue;
        if(tmp.size() == 0)continue;

        bool ck = false;
        int p = 0;
        for(int i = 0;i < tmp.size();i ++) {
            if(tmp[i] == 0)ck = true;
            p = (p + tmp[i] * ci[i]) % M;
        }

        if(ck)continue;
        if(p == i) {
            ans ++;
        }
    }

    std::cout << ans << '\n';
}

signed main()
{
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);

    int t = 1;std::cin >> t;
    while(t --)solve();

    return 0;
}

1005 - 咒语附魔(补题)

听说是 CF 的一个原题()
这个题,一开始拿 \(bitset\) 瞎搞了一发,意料之内地 TLE 了。
这个题,有一个你以为只是出题人偷懒但实际上就是做题的关键的信息:B 中每个位置上的数字等概率地生成为 \(0\)\(1\)
这意味着什么?
首先想一下,要想让结果尽可能地大,那是不是就是前缀连续 \(1\) 的长度尽可能地长?
那么,我们要找的,就是连续的前缀 \(1\) 的长度,又因为 B 中每个位置上的数字等概率地生成为 \(0\)\(1\),所以,每次都有 \(1/2\) 的概率,在该位置异或得到 \(0\),也就会跳出循环,那么,一定会很快就会跳出循环,所以循环次数一定不会多,也就是……暴力就行了。
找到最长的前缀 \(1\) 的长度,对这些串收集起来,排序取最大,数 \(1\) 的个数即可。

点击查看代码
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;

const int N = 2e5 + 9;

void solve()
{
    int n, m;std::cin >> n >> m;

    std::string s, t;std::cin >> s >> t;

    std::vector<std::vector<int>> g(n + 1);

    for(int i = 0;i + n - 1 < t.size();i ++) {
        int sum = 0;
        for(int j = i;j < t.size() && sum < n;j ++) {
            if(t[j] != s[j - i])sum ++;
            else break;
        }
        g[sum].push_back(i);
    }

    std::vector<std::string> ans;

    for(int i = n;i >= 0;i --) {
        if(g[i].size()) {
            for(auto &j : g[i]) {
                std::string tmp = "";
                for(int k = j;k <= j + n - 1;k ++) {
                    tmp += ((t[k] == s[k - j]) ? '0' : '1');
                }
                ans.push_back(tmp);
            }
            break;
        }
    }

    std::sort(ans.begin(), ans.end());
    int sum = 0;
    
    if(!ans.size()) {
        std::cout << 0 << '\n';
    } else {
        for(auto &i : ans[ans.size() - 1]) {
            sum += (i == '1');
        }
        std::cout << sum << '\n';
    }
}

int main()
{
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);

    int t = 1;std::cin >> t;
    while(t --)solve();

    return 0;
}
posted @ 2025-04-27 00:31  天天超方的  阅读(228)  评论(0)    收藏  举报