题解:P7912 [CSP-J 2021] 小熊的果篮
Luogu - P7912
考虑如何用链表维护每一个块的删除操作。
发现一个小规律:下一轮将会被删除的果子,一定是这一轮被删除的果子的下一个果子。
但仔细想发现,这一轮被删除的果子的下一个果子,下一轮不一定真的被删除,考虑如何去判断,
当满足以下两个条件的任意一个时,这一轮被删除的果子(假设为
-
当前被删除的果子的下一个果子会在当前轮结束后被合并,成为前一个块的中间部分(这样不在块头,就不会被删除)。
判断标准:当前被删除果子的前一个和后一个相同,说明删除当前果子后,当前果子的前、后两个不同的块会被合并。
代码:
a[lst[x].pre]==a[lst[x].nxt] -
当前被删除的果子的下一个果子会在当前轮被删除。
判断标准:当前果子和下一个果子种类不一样,说明它们都是块(果篮)的开头,都会在这一轮被删掉。
代码:
a[x]!=a[lst[x].nxt]
有了上面的基础实现就很简单了。可以每一轮处理出下一轮会被删除的果子的下标,内层循环中,每次删除上一轮处理出的当前轮会被删除的数,同时根据性质继续处理当前轮可能被删除的数。
时间复杂度:这样因为每个数只会被删除一次,复杂度稳定
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Node{
int pre, nxt; // 当前节点的前一个节点下标、后一个节点下标
};
void solve()
{
int n;
cin >> n;
vector <int> a(n + 2);
vector <Node> lst(n + 2); // 模拟双向链表的数组
vector <int> now; // 下一轮会被删除的果子的下标
a[0] = a[n + 1] = INT_MAX; // 避免程序判断为a[0]==a[1],a[n]==a[n+1]
for(int i = 1; i <= n; i ++){
cin >> a[i];
if(a[i] != a[i - 1]){ // 和前一个不相等,说明是当前块的块头,会被删除
now.push_back(i);
}
lst[i].pre = i - 1;
lst[i].nxt = i + 1;
}
lst[0].nxt = 1; // 链接lst[0]和lst[1]
// while(!now.empty()){ // 这样写也行,如果没有下一轮要被删除的元素了,也退出循环
while(lst[0].nxt <= n){ // 如果lst[0].nxt > n,说明链表中没有元素了,外层循环自然就结束了
vector <int> tmp; // 处理出的下一轮会被删除的果子的下标,是now的中继数组
for(int x : now){
cout << x << ' ';
lst[lst[x].nxt].pre = lst[x].pre; // 删除节点x
lst[lst[x].pre].nxt = lst[x].nxt;
if(a[lst[x].pre] != a[lst[x].nxt] && a[x] == a[lst[x].nxt]){ // 当前被删除的果子的下一个果子不会在当前轮结束后被合并 且 当前被删除的果子的下一个果子不会在当前轮被删除
tmp.push_back(lst[x].nxt); // nxt下一轮会被删除
}
}
cout << "\n";
now = tmp;
}
}
signed main()
{
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
solve();
return 0;
}
End
管理员大大辛苦啦~
谢谢大家!