1 条题解
-
0
思路与算法
维护仍存在水果的双向链表及当前块首。每轮先输出全部块首,再同时删去,借助后继并查集跳过被删位置,只保留新块首,自动处理同色合并。
正确性说明
初始相邻异色处恰是块首。删完一轮后,一个新块首必是某个旧块首的第一个存活后继;用双链表的真实前驱比较颜色,恰筛出合并后的块首。每轮先保存整组再删除实现同时性,归纳得到全部篮子及升序。
复杂度
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