题目链接: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
总结:这道题的思考还是非常有趣的,代码写起来也需要思考,非常适合我锻炼啦!!