不一样的牌堆(c++)

【题目描述】:

有一副牌,由 N 张卡片组成,卡片上写着从 1 到 N 的整数(无重复)。

你将进行 N 次操作(编号 1∼N),每次操作的步骤如下:

  1. 你将得到一张卡片,设 X 是写在上面的整数。
  2. 观察桌上已有的,每个牌堆最上面卡片上的数字,找到大于或等于 X,且最小的,将 X 正面朝上放在这一牌堆的最上面。如果找不到这样的牌堆,则用 X 单独建立一个牌堆。
  3. 当某个牌堆有 K 张牌时,将这个牌堆全部拿走,并在每张牌上记录拿走时的操作编号。

【输入描述】:

第一行两个正整数 N 和 K;

第二行是按照操作次序,你将得到的卡片上的数字。

【输出描述】:

输出 N 行,第 i 行,输出数字 i 被拿走的操作编号。

【样例输入1】:

5 2
3 5 2 1 4

【样例输出1】:

4
3
3
-1
4

【样例1说明】:

P=(3,5,2,1,4),K=2。

在第一步中,写有 3 的牌面朝上放在桌子上,不叠在任何牌上。

在第二步中,写有 5 的牌面朝上放在桌子上,不叠在任何牌上。

在第三步中,写有 2 的牌面朝上叠放在写有 3 的牌上。

现在有一堆由 2 张牌组成,上面写着从上面开始的 2 和 3,这些牌被拿走。

在第 4 步中,写有 1 的牌面朝上叠放在写有 55 的牌上。

现在有一堆由 2 张牌组成,上面写着从上面开始的 1 和 5,这些牌被拿走。

在第五步中,写有 4 的牌面朝上放在桌子上,不叠在任何牌上。

写着 4 的牌直到最后都没有被拿走。

【样例输入2】:

15 3
3 14 15 9 2 6 5 13 1 7 10 11 8 12 4

【样例输出2】:

9
9
9
15
15
6
-1
-1
6
-1
-1
-1
-1
6
15

【时间限制、数据范围及描述】:

时间:1s 空间:256M

对于 30% 的数据:1≤K≤N≤1e4;

对于100% 的数据:1≤K≤N≤2×1e5;

分析与解:

set来维护桌子上的每叠牌。因为每叠牌被看到的只有最上面那张,所以每叠牌在set里只用那最上面的牌来表示。利用set递增排序的性质,我们按题意每次递牌的时候可用 lower_bound 二分去找最小的比 x 大或者等于的牌号,然后做盖住操作(set删除那个牌号并插入新牌 x),如果找不到则将 x 直接插入set

除此之外,由于本题还要求每叠牌数目达到 K 时要被整叠删除,因此我们还必须开一个数组去维护某张牌对应的那叠牌的数目,每次在做set操作的同时去对这个数组做更新。另外,还需要一个链表顺序记录这一叠牌,方便在整叠删除时,给每个牌记录删除时间。

好了,贴上代码:

#include <bits/stdc++.h>
#define lc(a) (a)<<1
#define rc(a) (a)<<1|1
#define Mid int mid=l+r>>1
#define ll long long
#define Mod 1000000007
#define Max 1145141919
#define LLMax 9223372036854775807
using namespace std;
//I can All_Killed International Olmpiad in Informatics.
inline int in(){
	char c=getchar();int f=1;int x;
	while((c<'0'||c>'9')&&c!='-') c=getchar();
	if(c=='-')f=-1,c=getchar();
	for(x=0;c>='0'&&c<='9';c=getchar())
		x=(x<<3)+(x<<1)+(c^48);
	return x*f;
}
template <typename T>
inline void in(T &x){
	char c=getchar();int f=1;
	while((c<'0'||c>'9')&&c!='-') c=getchar();
	if(c=='-')f=-1,c=getchar();
	for(x=0;c>='0'&&c<='9';c=getchar())
		x=(x<<3)+(x<<1)+(c^48);
	x*=f;
}
const int N=2e5+5,M=1e3+5;
struct Top{
	int v,id;
	bool operator<(const Top &a)const{
		return v<a.v;
	}
};
set<Top> st;
vector<int> a[N];
int f[N];
int main(){
	freopen("card.in","r",stdin);
	freopen("card.out","w",stdout);
	int n=in(),k=in(),cnt=0;
	for(int i=1;i<=n;i++){
		Top x;in(x.v),x.id=114514;
		set<Top>::iterator it=st.lower_bound(x);
		if(it==st.end()){
			a[++cnt].push_back(x.v);
			Top tmp=Top{x.v,cnt};
			st.insert(tmp);
			if(a[cnt].size()>=k){
				st.erase(tmp);
				for(auto j:a[cnt]) f[j]=i;
			}
		}
		else{
			int p=it->id;
			Top tmp=Top{x.v,p};
			st.erase(*it),st.insert(tmp),it=st.find(tmp);
			a[p].push_back(x.v);
			if(a[p].size()>=k){
				st.erase(*it);
				for(auto j:a[p]) f[j]=i;
			}
		}
	}
	for(int i=1;i<=n;i++)
		printf("%d\n",f[i]==0?-1:f[i]);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值