1 条题解
-
1
需要实现频繁的删除,我们考虑使用链表维护。且还牵扯到块的合并,所以对于每一个块维护一个块的链表。我们的两个链表长这个样子。
有了这样就好办了,每次取走一个水果就使用双向链表的删除操作即可。需要注意删除以后,我们要重新更新一下当前块的 这两个信息,
例如删去第一个块最左边黑色后, 也要修改,如果这个块只有一个元素那么就不用管了,反正后续我们会重新合并块的信息。
对于合并相同种类的块,我们对链表就正常往后跳,当跳到某一个节点后,当前节点和左边节点的值相同说明属于同一个水果,且当前节点是当前块的起点,说明这两个块需要合并,那我们就修改块链表的信息
#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 2e5 + 5; int n, a[N]; int l[N], r[N], bl[N], br[N]; int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; a[0] = a[n + 1] = -1;// 设为 -1 避免和第一个比较 r[0] = 1, l[n + 1] = n; for (int i = 1; i <= n; i++) l[i] = i - 1, r[i] = i + 1; for (int i = 1; i <= n; i++)//双指针求每一个块的范围 i ~ j { int j = i; while (j + 1 <= n && a[j + 1] == a[i]) j++; br[i] = j, bl[j] = i; i = j; } int tot = 0; while (tot < n) { for (int i = r[0]; i != n + 1; i = r[i]) { tot++; cout << i << " "; int u = l[i], v = r[i]; // u是i的左边点编号,v是右边点编号 r[u] = v, l[v] = u; // 删掉点 i if (a[v] == a[i]) // 修改块的链表 { br[v] = br[i], bl[br[i]] = v; } i = br[i]; //先跳到块的末尾,循环结束后i = r[i] 去往下一个块 } cout << "\n"; for (int i = r[0]; i != n + 1; i = r[i]) { if (a[i] == a[l[i]] && bl[br[i]] == i) //当前点 a[i] 和左边节点值相同,且当前点是块的起点,块的起点是如何判断的? //首先 br[i] 可以获取到块的终点,然后终点的左连接bl[块终点] 等于 i 本身说明 i 为块起点 { int ed = l[i]; // 上一个块的终点 int st = i; // 当前块的起点 br[bl[ed]] = br[st]; bl[br[st]] = bl[ed]; } i = br[i];//先跳到块的末尾,循环结束后i = r[i] 去往下一个块 } } return 0; }
- 1
信息
- ID
- 1357
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 271
- 已通过
- 101
- 上传者