题目翻译

依次摸 $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;
}
最后修改:2023 年 04 月 22 日
v我50吃疯狂星期四