2026牛客寒假算法基础集训营5

链接:https://ac.nowcoder.com/acm/contest/120565

题目按照通过人数降序排序。

B – 智乃的瓷砖

算法:

模拟。

思路:

无。

关键代码:

void miyan()
{
    int n, m;
    cin >> n >> m;
    vector<string> g(n, string(m, '/'));
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < m; ++j)
        {
            if (i == 0 && j == 0)
                continue;

            if (j == 0)
            {
                if (g[i - 1][j] == '/')
                    g[i][j] = '\\';
                else
                    g[i][j] = '/';
            }
            else
            {
                if (g[i][j - 1] == '/')
                    g[i][j] = '\\';
                else
                    g[i][j] = '/';
            }
        }
    }

    for (auto s : g)
    {
        for (auto c : s)
            cout << c;

        cout << endl;
    }
}

J – 智乃的幻方

算法:

模拟。

思路:

无。

关键代码:

void miyan()
{
    vector a(3, vector<int>(3));
    for (int i = 0; i < 3; ++i)
        for (int j = 0; j < 3; ++j)
            cin >> a[i][j];

    int sum = accumulate(all(a[0]), 0);
    int cnt1 = 0, cnt2 = 0;
    set<int> S;
    for (int i = 0; i < 3; ++i)
    {
        cnt1 += a[i][i];
        cnt2 += a[i][2 - i];

        int tot1 = 0, tot2 = 0;
        for (int j = 0; j < 3; ++j)
        {
            S.insert(a[i][j]);
            tot1 += a[i][j];
            tot2 += a[j][i];
        }

        if (tot1 != sum || tot2 != sum)
        {
            cout << "No" << endl;
            return;
        }
    }

    if (cnt1 != sum || cnt2 != sum)
    {
        cout << "No" << endl;
        return;
    }

    if (S.size() == 9 && *S.begin() == 1 && *S.rbegin() == 9)
        cout << "Yes" << endl;
    else
        cout << "No" << endl;
}

G – 智乃的箭头魔术

算法:

模拟。

思路:

无。

关键代码:

void miyan()
{
    string s = "0112233445142015320125410214530214510214102302142025101203201451451522302514203214510021454101002532";

    int now = 0;
    for (int i = 0; i < s.size(); ++i)
    {
        if (s[i] == '0')
        {
            if (now == 0)
                now = 3;
            else if (now == 1)
                now = 2;
            else if (now == 2)
                now = 1;
            else
                now = 0;
        }
        else if (s[i] == '1')
        {
            if (now == 0)
                now = 0;
            else if (now == 1)
                now = 3;
            else if (now == 2)
                now = 2;
            else
                now = 1;
        }
        else if (s[i] == '2')
        {
            if (now == 0)
                now = 1;
            else if (now == 1)
                now = 0;
            else if (now == 2)
                now = 3;
            else
                now = 2;
        }
        else if (s[i] == '3')
        {
            if (now == 0)
                now = 2;
            else if (now == 1)
                now = 1;
            else if (now == 2)
                now = 0;
            else
                now = 3;
        }
        else if (s[i] == '4')
        {
            if (now == 0)
                now = 1;
            else if (now == 1)
                now = 2;
            else if (now == 2)
                now = 3;
            else
                now = 0;
        }
        else
        {
            if (now == 0)
                now = 3;
            else if (now == 1)
                now = 0;
            else if (now == 2)
                now = 1;
            else
                now = 2;
        }

        cout << now;
    }
}

D – 智乃的果子

算法:

贪心,堆。

思路:

经典贪心题目。

原题:https://www.luogu.com.cn/problem/P1090

贪心策略:从最小的开始合并。

过程使用堆模拟即可,细节看代码。

关键代码:

using pll = pair<ll, ll>;

void miyan()
{
    int n;
    cin >> n;

    priority_queue<pll, vector<pll>, greater<>> q;
    for (int i = 0; i < n; ++i)
    {
        ll c, w;
        cin >> c >> w;
        q.push({w, c});
    }

    ll ans = 0;
    while (q.size())
    {
        auto [w, c] = q.top();
        q.pop();

        while (q.size() && q.top().first == w)
        {
            auto [nw, nc] = q.top();
            q.pop();

            c += nc;
        }

        ll t = c / 2;

        if (t)
        {
            ans = (ans + 2ll * t % MOD * w % MOD) % MOD;

            q.push({w * 2ll, t});
        }

        if ((c & 1) && q.size())
        {
            auto [nw, nc] = q.top();
            q.pop();

            ans = (ans + w + nw) % MOD;

            q.push({w + nw, 1});

            if (nc > 1)
                q.push({nw, nc - 1});
        }
    }

    cout << ans << endl;
}

F – 智乃的算法竞赛群友

算法:

贪心,\(dp\)。

思路:

只有三种填充策略:

  • 填充 \(qcjjkktd\),贡献 \(a + b\) 快乐值;
  • 填充 \(qcjjkkt\),贡献 \(a\) 快乐值;
  • 填充 \(td\),贡献 \(b\) 快乐值。

三种填充的长度分别为 \(2, 7, 8\),其最大公约数为 \(56\),因此对于长度为 \(56 * k\) 的区间,可以直接贪心使用任意一种策略填满。

而剩余的部分长度必然小于 \(56\),可以直接暴力填充,考虑 \(dp\) 填充剩余的部分。

定义 \(dp[i]\) 为填充前 \(i\) 个位置的最大快乐值是多少。

容易得到状态转移公式为:\(dp[i] = max({dp[i – 2] + b, dp[i – 7] + a, dp[i – 8] + a + b})\)。

注意边界情况。

剩余的长度小于 \(56\) 可能会出一些问题,考虑剩余的长度再加上 \(56\)。

关键代码:

void miyan()
{
    ll n, a, b;
    cin >> n >> a >> b;

    ll ans = 0;
    if (n > 112)
    {
        ll m = (n / 56 - 1) * 56;

        ans = max({m / 2 * b, m / 7 * a, m / 8 * (a + b)});

        n -= m;
    }

    vector<ll> dp(n + 1, 0);
    for (int i = 1; i <= n; ++i)
    {
        if (i >= 2)
            dp[i] = max(dp[i], dp[i - 2] + b);
        if (i >= 7)
            dp[i] = max(dp[i], dp[i - 7] + a);
        if (i >= 8)
            dp[i] = max(dp[i], dp[i - 8] + a + b);
    }

    cout << ans + dp[n] << endl;
}

E – 智乃的最大子段和取模

算法:

\(STL\),贪心,二分,分类讨论。

思路:

注意:题目为 \(0-based\),以下题解为 \(1-based\)。

在模 \(p\) 意义下 \([l, r]\) 子段和为 \((s[r] – s[l – 1] + p) \text{ mod } p\)(\(s[x]\) 表示数组下标 \([1, x]\) 范围的和),对于每个 \(r\) 我们可以找到满足条件最大的 \(l\) 并更新全局答案,使得 \((s[r] – s[l – 1] + p) \text{ mod } p\) 的值最大。

在模 \(p\) 的意义下,\(s[l – 1]\) 的值可能大于 \(s[r]\)、也可能小于或者等于 \(s[r]\),分类讨论:

  • 当 \(s[r] = s[l – 1]\) 时,子段和为 \(0\),可以直接忽略该情况;
  • 当 \(s[r] > s[l – 1]\) 时,子段和为 \(s[r] – s[l – 1]\),想让子段和最大,就要最小化 \(s[l – 1]\),由于 \(s[0] = 0\),因此 \(l – 1\) 可以直接选择 \(0\),那么这种情况的最优解就是 \([1, r]\);
  • 当 \(s[r] < s[l – 1]\) 时,子段和为 \(s[r] + p – s[l – 1]\),想让子段和最大,需要让 \(s[l – 1]\) 满足大于 \(s[r]\),且尽可能小,因此直接找大于 \(s[r]\) 的第一个 \(s[l – 1]\) 即可。

对于第三种情况可以使用 \(map\) 优化寻找过程,使得题目可以在 \(O(logn)\) 时间内找到满足条件的 \(s[l – 1]\)。

关键代码:

void miyan()
{
    int n, p;
    cin >> n >> p;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i)
        cin >> a[i];

    vector<int> sum(n + 1, 0);
    for (int i = 1; i <= n; ++i)
        sum[i] = (sum[i - 1] + a[i]) % p;

    int ans = 0, L = 0, R = 0;
    map<int, int> hash;
    hash[0] = 0;
    for (int i = 1; i <= n; ++i)
    {
        int cur = sum[i] - 0;
        if (cur > ans)
        {
            ans = cur;
            L = 1, R = i - 1;
        }

        auto it = hash.upper_bound(sum[i]);

        if (it != hash.end())
        {
            auto [val, pos] = *it;

            int cur = (1ll * sum[i] + p - val) % p;

            if (cur > ans)
            {
                ans = cur;
                L = pos, R = i - 1;
            }
        }

        if (!hash.count(sum[i]))
            hash[sum[i]] = i;
    }

    cout << L << ' ' << R << ' ' << ans << endl;
}

I – 智乃挖坑

算法:

等差数列差分,二分。

思路:

对于每次挖坑的序偶 \(<p, f>\) 其实就是在数组上进行两次等差数列修改,既 \((1, f)\) 和 \((f – 1, 1)\):

  • 其中 \((1, f)\) 为在数组下标 \([p – f + 1, p]\) 范围上减去一个首项为 \(1\),尾项为 \(f\),公差为 \(1\) 的等差数列;
  • \((f – 1, 1)\) 同理;
  • 未考虑边界情况,读者请自行考虑。

以上直接套用等差数列差分模板即可,其可以在 \(O(1)\) 时间内对数组进行等差数列修改。

等差数列差分讲解:前缀和详解及其拓展 – MiyanLog

对于第几次挖坑挖出边界,可以使用二分答案找出最后一次未挖出边界的操作的下标。

具体实现看代码。

关键代码:

using pll = pair<ll, ll>;

void miyan()
{
    ll n, m, h;
    cin >> n >> m >> h;

    vector<pll> a(m + 1);
    for (int i = 1; i <= m; ++i)
        cin >> a[i].first >> a[i].second;

    auto add = [&](vector<ll> &b, int l, int r, ll s, ll e) -> void
    {
        ll d = 0;
        if (l < r)
            d = (e - s) / (r - l);

        b[l] += s;
        b[l + 1] += d - s;
        b[r + 1] += -e - d;
        b[r + 2] += e;
    };

    auto check = [&](int x) -> bool
    {
        vector<ll> b(n + 3, 0);
        for (int i = 1; i <= x; ++i)
        {
            auto [p, f] = a[i];

            int L1 = max(1ll, p - f + 1);
            int R1 = p;
            ll s1 = f - p + L1;
            ll e1 = f;

            add(b, L1, R1, s1, e1);

            if (p < n && f > 1)
            {
                int L2 = p + 1;
                int R2 = min(n, p + f - 1);

                ll s2 = f - 1;
                ll e2 = f + p - R2;

                add(b, L2, R2, s2, e2);
            }
        }

        for (int i = 1; i <= n; ++i)
            b[i] += b[i - 1];
        for (int i = 1; i <= n; ++i)
            b[i] += b[i - 1];

        for (int i = 1; i <= n; ++i)
            if (b[i] > h)
                return 0;

        return 1;
    };

    int l = 0, r = m;
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid))
            l = mid;
        else
            r = mid - 1;
    }

    if (l == n)
        cout << "No" << endl;
    else
        cout << "Yes" << endl
             << l + 1 << endl;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇