1 条题解

  • 0
    @ 2026-10-26 22:50:07

    思路与算法

    维护仍存在水果的双向链表及当前块首。每轮先输出全部块首,再同时删去,借助后继并查集跳过被删位置,只保留新块首,自动处理同色合并。

    正确性说明

    初始相邻异色处恰是块首。删完一轮后,一个新块首必是某个旧块首的第一个存活后继;用双链表的真实前驱比较颜色,恰筛出合并后的块首。每轮先保存整组再删除实现同时性,归纳得到全部篮子及升序。

    复杂度

    O(n α(n))摊还时间、O(n)空间,含链表与后继并查集初始化及全部n个编号输出。

    易错点

    篮子按行区分,不能把所有编号合成一行。后继查找用迭代路径压缩,避免长删除链递归溢栈。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        vector<int> a(n + 2), left(n + 2), right(n + 2), p(n + 2), heads;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            left[i] = i - 1;
            right[i] = i + 1;
            if (i == 1 || a[i] != a[i - 1])
                heads.push_back(i);
        }
        iota(p.begin(), p.end(), 0);
        auto find = [&](int x) {
            while (p[x] != x) {
                p[x] = p[p[x]];
                x = p[x];
            }
            return x;
        };
        while (!heads.empty()) {
            for (int i = 0; i < (int)heads.size(); i++) {
                if (i)
                    cout << ' ';
                cout << heads[i];
            }
            cout << "\n";
            for (int u : heads) {
                int l = left[u], r = right[u];
                right[l] = r;
                left[r] = l;
                p[u] = find(u + 1);
            }
            vector<int> next;
            for (int u : heads) {
                int v = find(u + 1);
                if (v <= n && (left[v] == 0 || a[left[v]] != a[v]) &&
                    (next.empty() || next.back() != v))
                    next.push_back(v);
            }
            heads.swap(next);
        }
    }
    
    • 1

    【21CSPJ普及组】小熊的果篮(fruit)

    信息

    ID
    1919
    时间
    1000ms
    内存
    512MiB
    难度
    普及
    标签
    递交数
    0
    已通过
    0
    上传者