模拟--不要被时间复杂度劝退

题目链接:https://codeforces.com/problemset/problem/1920/D

题目大意

开始我们有一个空的数组,我们一共要进行n次操作。每次操作有两种类型:

1.在数组末尾添加数字x

2.将数组复制x次并加在原数组后面

操作完后,会进行q次询问,每次询问我们要回答这个数组第k位的元素。

(1<=k<=min(1e18,原数组长度))

思考过程

这个数组会相当大,但是好在我们只需要找最多1e18位的数字。但是该怎么查询呢?

首先我想到,要查询第k位元素我们肯定要先找到最大的长度不超过k的数组(二分查找),这样我们就可以对它取模,从而把里面重复的那部分忽略掉。设k1为取模后的结果,那么现在问题就边成了寻找第k1位元素。可以发现,问题是具有递归性质的。

很显然如果取模后等于0,说明那个时期的数组最后一位刚好满足条件,这一位数字就是答案。

时间复杂度分析

这样的时间复杂度是O(qlogNlogN),我们观察刚才的递归,每次向下递归时分为两种情况:

1.此时的数组最后一位满足条件,这时直接return了

2.不满足条件,那就会取模,这时原数字的大小已经被减去了x倍,x取决于数组翻的倍数(>=2),那么最多就是log2(1e18);

由于每次递归还需要二分查找,所以整体时间复杂度为O(q*logN*logN);

代码实现

这道题代码还是需要一点熟练度的。从上面我们想要实现的内容入手,首先我们需要筛出数组长度小于1e18的时期。这里我们可以先用cnt记录长度,由于x<=1e9,我还是比较担心会有爆longlong的情况,这里cnt我直接使用__int128类型。然后用vector存储这些时期,需要存储这个时期数组长度和数组末尾值这两样东西。然后就向上面那样递归即可

提交记录:https://codeforces.com/contest/1920/submission/283363550

总结:这道题的思考还是非常有趣的,代码写起来也需要思考,非常适合我锻炼啦!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值