题面
有一副正面朝下的 N 张扑克牌,每张牌的正面数字构成一个1∼N 的全排列,从上到下的第 i 张牌数字为 p i p_i pi用这副牌,做 N 次操作,每次操作包含以下几步:
选择这幅牌最顶部的扑克牌,令该扑克牌上的数字为 X。
对于桌面上的正面朝上的扑克牌堆中,将该扑克牌放在所有顶部数字至少为 X 的牌堆中顶部数字最小的那堆的顶部。如果不存在这样的牌堆,将其正面朝上放在桌面上,构成一个新的牌堆。
接着,如果存在一个扑克牌数量为 K 的牌堆,将这堆牌移出桌面。
对于每张扑克牌,请你求出在第几次操作后被移出桌面,如果没有移出,输出 -1。输出时,第 i 行为带有数字 i 的对应扑克牌的答案。
题意
有n张卡片n此操作,如果当前朝上的牌堆中的顶面上没有大于当前这张卡片时,这张牌就单独一堆,否则就找到第一个大于当前牌的堆将这张牌放在这堆牌的最上面,当某堆牌的数量魏k时就移走这堆牌。问每一张牌是在第几次操作拿走了,询问顺顺序时1到n。
思路
模拟,用cnt数组记录每一堆牌的有多少张,to数组代表每张牌的下面那张牌是谁,再采用set记录每一堆牌的最上面是哪张,每次操作二分查找set里的值即可,ans记录答案。
代码
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr)
#define int long long
using namespace std;
signed main()
{
IOS;
int n, k;
cin >> n >> k;
vector<int> v(n + 10), ans(n + 10, -1), cnt(n + 10), to(n + 10);
//ans初始化-1有没有答案的牌
for (int i = 0; i < n; i++) cin >> v[i];
set<int> s;
for (int i = 0; i < n; i++)
{
auto it = s.lower_bound(v[i]);//查找这一张牌应该放在哪一堆
if (it == s.end())//如果没有找到相应的牌堆就是单独一堆
{
s.insert(v[i]);
cnt[v[i]] = 1;//这一堆的大小当然为1
}
else
{
//如果找到了是哪一堆了那么这堆牌的顶面应该改变并且堆的大小+1
cnt[v[i]] = cnt[*it] + 1;
//更新一下这张牌的下面那张就是查找到的
to[v[i]] = *it;
//更改这堆牌的顶面大小
s.erase(it);
s.insert(v[i]);
}
if (cnt[v[i]] == k)
{
//记录这堆牌的操作数是是几
for (int j = v[i]; j; j = to[j]) ans[j] = i + 1;
s.erase(s.find(v[i]));
}
}
for (int i = 1; i <= n; i++) cout << ans[i] << endl;
return 0;
}