考点:链表模拟
根据题意,每一轮都需要删除题目要求的数,然后将剩余的数连起来,序号重新规划,因此我们可以想到链表这一数据结构,可以实现O(1)的时间复杂度的删除操作,可以发现,除了第一次要求删除序号为2的倍数的结点以外,其余的都是要求删除序号为最后一个幸运数的值的倍数的结点。每一轮我们可以删除若干个节点,以此类推,直到不能再删除了为止。考虑到题意,我们删除的结点序号不会超过右边界n,因此在删除结点的循环中我们只在n以内进行删除即可。这个算法的时间复杂度介于O(n^2)和O(nlogn)之间,题目的数据范围是m < n < 1000 * 1000,理论上有可能超时,但官方的数据较弱,顺利通过。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1000010;
int l[N], r[N];
//删除位置p的结点
void del(int p){
r[l[p]] = r[p];
l[r[p]] = l[p];
}
int n, m;
void slove(){
for(int i = 1; i < N - 1; i++){
l[i] = i - 1, r[i] = i + 1;
}
int s = 1, t = 1; //s就指向当前最后一个幸运数,t表示收录的幸运数个数
while(r[s]){
if(s >= n) break;
int d = r[s] == 2 ? 2 : s; //第一次比较特殊
int cnt = t++; //序号从cnt开始数
int cur = r[s], ne;
while(cur < n){
cnt++;
ne = r[cur];
if(cnt % d == 0){
del(cur);
}
cur = ne;
}
s = r[s];
}
}
int main(){
scanf("%d %d", &m, &n);
slove();
int ans = 0;
for(int i = 1; i < n; i = r[i]){
if(i > m){
ans++;
}
}
printf("%d\n", ans);
return 0;
}