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

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

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

A – 比赛安排

算法:

模拟。

思路:

连续三场比赛类型不相同,那么就要是形如 \(1\) \(2\) \(3\) \(1\) \(2\) \(3\) 这种循环,容易想到最多的个数不能比最少的个数多两个及以上,因此只需判断最多的个数是不是比最小的个数多 \(1\) 个以内。

关键代码:

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

    if (max({a, b, c}) - min({a, b, c}) <= 1)
        cout << "YES" << endl;
    else
        cout << "NO" << endl;
}

B – NCPC

算法:

贪心。

思路:

如果最大值出现了奇数次,那么最后剩余的肯定是最大值,那么答案中只有最大值是 \(1\)。

如果最大值出现了偶数次,那么最大值肯定保留不下(既为 \(0\)),其余的任意元素都可以保留(既为 \(1\)):

  • 选择一个不是最大值的 \(x\),保留任意一个,其余全部元素(除了保留的一个 \(x\))轮流与最大值进行对决,由于最大值出现了偶数次,最后剩余的一个元素必然是保留的 \(x\)。

关键代码:

void miyan()
{
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto &x : a)
        cin >> x;

    map<int, int> m;
    for (auto x : a)
        ++m[x];

    auto b = a;
    ranges::sort(b);
    b.erase(unique(all(b)), b.end());

    map<int, int> st;
    bool ok = 1;
    for (int i = b.size() - 1; i >= 0; --i)
    {
        if (i == b.size() - 1)
        {
            if (m.count(b[i]) && m[b[i]] % 2 == 1)
            {
                st[b[i]] = 1;
                ok = 0;
            }
        }
        else if (ok)
        {
            st[b[i]] = 1;
        }
    }

    string ans = string(n, '0');
    for (int i = 0; i < n; ++i)
    {
        if (st.count(a[i]) && st[a[i]])
            ans[i] = '1';
    }

    cout << ans << endl;
}

I – 01回文

算法:

贪心。

思路:

结论:如果 \(a[i][j]\) 只出现了 \(1\) 次其必然不存在满足条件的 \((x, y)\),其余情况全部存在。

证明:由于 \(a[i][j]\) 出现了不止一次且只有 \(0\) 和 \(1\) 两种元素,那么 \(a[i][j]\) 到达距离它最近的相同元素的路径必然是回文的,其中间由 \(k(k >= 0)\) 个非 \(a[i][j]\) 元素组成,结尾是 \(a[x][y](a[x][y] = a[i][j])\)。

关键代码:

void miyan()
{
    int n, m;
    cin >> n >> m;
    vector<string> g(n + 1);
    for (int i = 1; i <= n; ++i)
    {
        cin >> g[i];
        g[i] = ' ' + g[i];
    }

    if (n == 1 && m == 1)
    {
        cout << 'Y' << endl;
        return;
    }

    int cnt0 = 0, cnt1 = 0;
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= m; ++j)
        {
            if (g[i][j] == '1')
                ++cnt1;
            else
                ++cnt0;
        }
    }

    if (cnt0 == 1 || cnt1 == 1)
    {
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= m; ++j)
            {
                if (g[i][j] == '0' && cnt0 == 1)
                    cout << 'N';
                else if (g[i][j] == '1' && cnt1 == 1)
                    cout << 'N';
                else
                    cout << 'Y';
            }

            cout << endl;
        }

        return;
    }

    vector<string> ans(n, string(m, 'Y'));
    for (auto s : ans)
    {
        for (auto c : s)
            cout << c;
        cout << endl;
    }
}

F – x?y?n!

算法:

贪心,位运算。

思路:

观察题目样例可得出一个 \(guess\):\(x \oplus y = n, y = x + n\)。

那怎么确定 \(x\) 呢?

已知 \(x \oplus y = n\),移项得 \(y = x \oplus n\),那么需要满足 \(y = x + n\) 与 \(y = x \oplus n\) 两个式子,继续展开:

  • \(y = x + n \Rightarrow y = x|n + x\text{&}n\)
  • \(y = x \oplus n \Rightarrow y = x|n – x\text{&}n\)

那么只需满足 \(x\text{&}n\) 为 \(0\) 即可。

考虑到 \(n\) 为 \(int\) 类型,其最大有 \(31\) 位,只需令 \(x\) 等于 \(n\) 左移 \(31\) 位即可,\(y\) 易得。

关键代码:

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

    cout << (n << 31ll) << ' ' << (n << 31ll | n) << endl;
}

H – 权值计算

算法:

贡献法,贪心。

思路:

伪代码计算的是:数组中每个前缀中不同元素个数的和。

经典贡献法题目。

考虑对数组所有不同子数组中 \(a[i]\) 的贡献找规律。

对于数组 \(a = {1, 2, 3, 2, 2, 4}\),\(a[i]\) 在 \(l = [1, n]\),\(r = [l, n]\) 子数组的贡献为:

l\r123456
1123334
212223
31223
4112
512
61

容易发现,对于先前从未出现过的元素,其在当前列会相较于上一列增加 \(i\) 的贡献,而出现过的元素会相较于上一列增加 \(i – pos\) 的贡献(\(pos\) 为上一次出现的位置),第 \(i\) 列的总贡献为 \(sum_i * (n – i + 1)\)。

关键代码:

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

    map<int, int> m;

    ll ans = 0;
    ll sum = 0;
    for (int i = 1; i <= n; ++i)
    {
        ll last = 0;
        if (m.count(a[i]))
            last = m[a[i]];

        m[a[i]] = i;

        sum += i - last;

        ans += sum * (n - i + 1);
    }

    cout << ans << endl;
}

E – 01矩阵

算法:

构造。

思路:

难度对于每个人来说差距很大,脑电波对上直接秒的题。

观察以下规律:

/*
2
00
01

3
000
011
010

4
0000
0111
0100
0101

5
00000
01111
01000
01011
01010
*/

关键代码:

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

    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < n; ++j)
            cout << min(i, j) % 2;

        cout << endl;
    }
}

J – 终于再见

算法:

多源 \(bfs\)。

思路:

对于每个点都 \(bfs\) 找比它等级高的点的时间复杂度为 \(O(n * (n + m))\)。

引入性质:在任意简单无向图中,不同度数的个数 \(k\) 满足 \(k ≤ 2 \sqrt n\)。

此时,我们根据度数对点进行分组,逆序对每个组进行多源 \(bfs\),每组的复杂度均为 \(O(n + m)\),因此时间复杂度优化至 \(O(\sqrt n * (n + m))\)。

具体实现:

  • 首先根据度数对点进行分组,逆序对每个组进行多源 \(bfs\);
  • 定义 \(dist_u\) 为 \(u\) 点到更高度数点的最短距离;
  • 对于当前组的点 \(u\),如果其 \(dist_u\) 为 \(\infty\),说明在当前连通分支中,没有比其度数高的点,得 \(u\) 点答案为 \(-1\);
  • 否则其答案就为 \(dist_u\)(因为在先前比它度数高的点都遍历完了,其 \(dist\) 就为答案);
  • 多源 \(bfs\) 时,只向度数更低的点走。

关键代码:

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

    vector<vector<int>> g(n + 1);
    vector<int> du(n + 1, 0);
    for (int i = 1; i <= m; ++i)
    {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
        ++du[u], ++du[v];
    }

    map<int, deque<int>, greater<>> hash;
    for (int i = 1; i <= n; ++i)
        hash[du[i]].push_back(i);

    vector<int> ans(n + 1);
    vector<int> dist(n + 1, 1e9);
    for (auto [x, q] : hash)
    {
        for (auto u : q)
        {
            if (dist[u] == 1e9)
            {
                ans[u] = -1;
                dist[u] = 0;
            }
            else
            {
                ans[u] = dist[u];
                dist[u] = 0;
            }
        }

        while (q.size())
        {
            auto u = q.front();
            q.pop_front();

            for (auto v : g[u])
            {
                if (du[v] >= x)
                    continue;

                if (dist[v] > dist[u] + 1)
                {
                    dist[v] = dist[u] + 1;
                    q.push_back(v);
                }
            }
        }
    }

    for (int i = 1; i <= n; ++i)
        cout << ans[i] << ' ';
    cout << endl;
}

暂无评论

发送评论 编辑评论


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