题目翻译
依次摸 $n$ 张卡片,第 $i$ 个卡片上写的 $p_i$,$p_i$ 是 $1$ 到 $n$ 的一个排列。
维护一些牌堆,如果 $p_i$ 大于所有堆顶的牌,那么新开一堆只有 $p_i$。
否则在所有堆顶的牌,找到大于 $p_i$ 最小的一张,把 $p_i$ 放到这个堆的堆顶。
如果这一堆有恰好 $K$ 张牌,把这 $K$ 个牌的数字都标记上当前的时间 $i$,并把这堆删掉。
最后输出每个数字被标记的时间,如果没有被标记过就是 $-1$。
题目思路
如果我们真的模拟题意去开栈,肯定会 boom 的。
观察题意可得,所有操作只考虑牌堆顶,且卡片只要进入牌堆就不会变动。
因此我们只需维护牌堆顶即可。但这样时间复杂度仍然很高,我们可以把牌堆顶存入一个 set,每次查询即为 $\mathcal O(\log n)$ 了。
完整代码
#include<bits/stdc++.h>
using namespace std;
int a[200005];
int fa[200005],id,c[200005],d[200005];
bool f[200005];
int ans[200005];
set<int>s;
int main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(s.size()==0||a[i]>*s.rbegin())
{
s.insert(a[i]);
id++;
fa[a[i]]=id;
c[id]=a[i];
d[id]++;
if(d[id]==k)
{
f[id]=1;
ans[id]=i;
s.erase(a[i]);
}
}
else
{
auto it=s.upper_bound(a[i]);
int x=fa[*it];
if(f[x]==1)
{
continue;
}
c[x]=a[i];
d[x]++;
fa[a[i]]=x;
s.erase(it);
s.insert(a[i]);
if(d[x]==k)
{
f[x]=1;
ans[x]=i;
s.erase(a[i]);
}
}
}
for(int i=1;i<=n;i++)
{
cout<<(ans[fa[i]]==0?-1:ans[fa[i]])<<endl;
}
return 0;
}