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

比赛链接

本文发布于博客园,会跟随补题进度实时更新,若您在其他平台阅读到此文,请前往博客园获取更好的阅读体验。

跳转链接:https://www.cnblogs.com/TianTianChaoFangDe/p/18773190

开题 + 补题情况

很唐的一场比赛,前四个签到题都做了八百年,然后又被博弈论硬控了,

1002 - 学历史导致的

签到题,由于数据范围只有几十,所以直接枚举暴搜就行。
点击查看代码

cpp 复制代码
#include <bits/stdc++.h>
#define inf 2e18
#define int long long

const int N = 2e5 + 9;

std::string a[] = {"jia", "yi", "bing", "ding", "wu", "ji", "geng", "xin", "ren", "gui"};
std::string b[] = {"zi", "chou", "yin", "mao", "chen", "si", "wu", "wei", "shen", "you", "xu", "hai"};

void solve()
{
    std::string s;std::cin >> s;

    for(int i = 1984;i <= 2043;i ++) {
        int ch = i - 1984;
        int x = ch % 10;
        int y = ch % 12;
        std::string t = a[x] + b[y];
        if(s == t) {
            std::cout << i << '\n';
            return;
        }
    } 
}

1004 - 学 DP 导致的

注意到最长答案只会是 \(26\),所以最多复制26次就行。

乍一看以为要用单调栈优化法或线段树优化法找最长上升子序列,结果定睛一看,都是小写字母,那范围也就 \(26\),直接暴力 DP 就行了。
点击查看代码

cpp 复制代码
#include <bits/stdc++.h>
#define inf 2e18
#define int long long

const int N = 2e5 + 9;

int toint(std::string &s) {
    int res = 0;
    for(auto &i : s) {
        res = res * 10 + i - '0';
    }
    return res;
}

void solve()
{
    std::string s;std::cin >> s;
    std::string k;std::cin >> k;

    int x;
    if(k.size() > 2) {
        x = 100;
    } else {
        x = toint(k);
    }

    std::string tmp = s;
    for(int i = 1;i < x;i ++) {
        s += tmp;
    }

    std::vector<int> dp(27, 0);

    auto getint = [](const char c) -> int {
        return c - 'a' + 1;
    };

    int ans = 0;
    for(auto &i : s) {
        int ix = getint(i);
        for(int j = 0;j < ix;j ++) {
            dp[ix] = std::max(dp[ix], dp[j] + 1);
        }
        ans = std::max(ans, dp[ix]);
    }

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

1003 - 学数数导致的

一开始读错题了,写了半天,以为是找不同的下标构成的题目的构造,然后定睛一看是找不同的数字构成的,这难度一下就下降了不少。

我们可以动态维护一下当前前缀每个数字的总数,然后从后往前遍历,记录一下后面出现了多少个不同的正整数 \(sum\),然后对于每一个数字,如果有间隔 \(0\) 的它自己,就更新一下它的答案最大值,也就是当前的 \(sum\),最后求一个和。

这个题有一个很关键的点在于要以 \(0\) 为分界点清除每个数字作为 \(p\) 的数量,否则可能不间隔 \(0\) 也被算进去了。
点击查看代码

cpp 复制代码
#include <bits/stdc++.h>
#define inf 2e18
#define int long long

const int N = 1e6;

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

    for(auto &i : a) {
        std::cin >> i;
    }

    std::vector<int> cnt(N + 9, 0), all(N + 9, 0);
    std::vector<bool> vis(N + 9, 0);

    for(int i = 0;i < n;i ++) {
        cnt[a[i]] ++;
    }

    int ans = 0;
    int sum = 0;
    vis[0] = true;
    for(int i = n - 1;i >= 0;i --) {
        if(a[i] == 0) {
            cnt[a[i]] --;
            continue;
        }

        if(!cnt[0])break;

        int ix;
        for(int j = i;j >= 0 && a[j] != 0;j --) {
            cnt[a[j]] --;
            ix = j;
        }

        for(int j = i;j >= 0 && a[j] != 0;j --) {
            if(a[j] && cnt[a[j]]) {
                all[a[j]] = std::max(all[a[j]], sum);
            }
            sum += !vis[a[j]];
            vis[a[j]] = true;
        }
        i = ix;
    }

    for(int i = 1;i <= N;i ++) {
        ans += all[i];
    }

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

1005 - 学几何导致的

不难发现,垂直的,其实就是旋转了 \(90\) 度的,也就是说:\((180 / k) \times x = 90 \times p\),\(p\) 为奇数,移项消元后可以得到:\(2 \times x = k \times p\),式子左边一定是一个偶数,\(p\) 是一个奇数,若要让式子成立,\(k\) 必须是一个偶数,因此所有奇数 \(k\) 答案都是 \(0\)。

然后,我们求出的 \(x\),也就是一个 \(90\) 度的循环,那么我们按 \(x\) 进行分块,对于奇数块中的线,和它垂直的就是偶数块中的对应位置的线,对于偶数块中的线,和它垂直的就是奇数块中的对应位置的线。

我们首先将最后对 \(2 \times x\) 取模后不完整的块往前匹配特殊计算一下。

然后对于剩下的,为了避免计算重复,每个块只和后面的块匹配,不难发现对于奇数块可以得到一个 \(1\) ~ \(n / 2x\) 的公差为 \(1\) 的等差数列,对于偶数块可以得到一个 \(1\) ~ \(n / 2x - 1\) 的公差为 \(1\) 的等差数列,分别对等差数列求和后,再乘一个 \(x\) 将块中的元素个数的贡献加上来即可。
点击查看代码

cpp 复制代码
#include <bits/stdc++.h>
#define inf 2e18
#define int long long

const int N = 2e5 + 9;

void solve()
{
    int n, k;std::cin >> n >> k;
    int ans = 0;

    if(k & 1) {
        std::cout << 0 << '\n';
        return;
    }

    int x = k / 2;

    int ch = n % x;
    n -= ch;

    int p = n / x;
    if(p & 1) {
        int sum = n / (2 * x) + 1;
        ans += ch * sum;
    } else {
        int sum = n / (2 * x);
        ans += ch * sum;
    }

    ch = n % (2 * x);
    n -= ch;
    if(ch) {
        int sum = n / (2 * x);
        ans += ch * sum;
    }

    int sum = n / (2 * x);
    if((sum + 1) & 1) {
        ans += sum / 2 * (sum + 1) * x;
        ans += sum / 2 * (sum - 1) * x;
    } else {
        ans += (sum + 1) / 2 * sum * x;
        ans += (sum - 1) / 2 * sum * x;
    }

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

1006 - 学博弈论导致的(补题)

被博弈论创似了。

题目说了一大堆,但如果给红宝石赋值为 \(1\),蓝宝石赋值为 \(2\),宝箱赋值为 \(4\),就可以把题目中所有操作拿掉的值控制在 \(1\) ~ \(3\),然后就是一个 NIM 游戏了。

好像是很常用的套路,算是学到了。
点击查看代码

cpp 复制代码
#include <bits/stdc++.h>
#define inf 2e18
#define int long long

const int N = 2e5 + 9;

void solve()
{
    int x, y, z;std::cin >> x >> y >> z;

    if((x + 2 * y) % 4 == 0) {
        std::cout << "Bob\n";
    } else {
        std::cout << "Alice\n";
    }
}
相关推荐
天天超方的2 天前
【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(6)
做题记录
天天超方的10 天前
【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(5)
做题记录
天天超方的17 天前
【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
做题记录
天天超方的18 天前
【CF比赛记录】Codeforces Round 1013 (Div. 3)
cf·做题记录
天天超方的1 个月前
【CF VP记录】Codeforces Round 1008 (Div. 2)
做题记录
天天超方的1 个月前
【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
做题记录
天天超方的1 个月前
Codeforces Round 1007 (Div. 2) 比赛记录
cf·做题记录