题380.ABC260-D - Draw Your Cards


题380.ABC260-D - Draw Your Cards


一、题目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、题解

本题题意为每次递出一张写着X号的牌,将这张牌放到桌上,如果桌上有某叠牌处在最上面的牌上写的号比X来的大,则将递来的牌盖在那叠牌的上面,否则则将牌放在桌子的其他地方,即新开一叠牌。
依上述说法,我们可以想着用set来维护桌子上的每叠牌。因为每叠牌被看到的只有最上面那张,所以每叠牌在set里只用那最上面的牌来表示。利用set递增排序的性质,我们按题意每次递牌的时候可用lower_bound二分去找最小的比X大或者等于的牌号,然后做盖住操作(set删除那个牌号并插入新牌),如果找不到则直接插入set。对此上述与pta上的一题列车调度类似。
除此之外,由于本题还要求每叠牌数目达到K时要被整叠eat,因此我们还必须开一个数组去维护某张牌对应的那叠牌的数目,每次在做set操作的同时去对这个数组做更新。另外,还需要一个res数组来存每号牌被eat的次序为第几。
最后,由于只直接实现上述操作会超时,所以还需要加一个并查集来对res做操作
详细思路可见代码注释,具体代码如下:

#include <bits/stdc++.h>

using namespace std;

const int maxn=2e5+2;

int N,K;
set<int> s;
//cnt[i]表示写着i号的卡牌包括它下面有多少张卡片。res[i]<0表示i为根牌,它的相反数为它以及它下面的所有卡片的被eat掉的次序为第几,res[i]>0表示i的祖先牌为谁
int cnt[maxn],res[maxn];//初始res[i]都为0,因为我们这里用0表示还没有任何次序,即没被eat

int findRoot(int v)//并查集(带路径压缩
{
    if(res[v]<=0) return v;//当res[v]<=0时,写着v号的牌为根牌
    else return res[v]=findRoot(res[v]);
}

int main()
{
    scanf("%d%d",&N,&K);
    for(int i=1;i<=N;i++)
    {
        int p;
        scanf("%d",&p);
        auto it=s.lower_bound(p);//在set中用二分去找最小的大于等于p的数
        if(it!=s.end())//在set里找到了>=p的第一个数(必定是最小的那个>=p的数,因为是递增set)
        {
            int t=*it;
            cnt[p]=cnt[t]+1;//p此时放到了t的上面,盖住t,cnt[p]继承cnt[t],并+1
            res[t]=p;//p作为t的祖先牌
            s.erase(it);//t被p盖住所以没用了,后续看桌子上那叠牌也是看到p,所以把t从set删了
        }
        else cnt[p]=1;//如果找不到,则自己独立做一叠牌放在桌上
        s.insert(p);//将p牌入set
        if(cnt[p]==K)//当p牌所在的那叠牌数目等于K时,就将那叠牌eat掉,并赋予res
        {
            s.erase(p);
            res[p]=-i;//常见思想,p作为并查集根节点,以res为负数作为答案,表示第几个被eat
        }
    }
    for(int i=1;i<=N;i++)
    {
        int root=findRoot(i);//对于每个号的牌,我们先去找它的根
        if(!res[root]) printf("-1\n");//如果res=0则没有被eat,则输出-1
        else printf("%d\n",-res[root]);//反之输出它的相反数
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值