题解 P4901 【排队】
%%%……这道题蒟蒻想了好久啊…
写个题解纪念下,同时也为和我一样看不懂出题人题解的oier们指点一条新的道路
肯定还是要先预处理出每个数质因数的个数,这个用线性筛可以解决,因为它是一个“和性函数”。此外,还要预处理出斐波那契数列,大概40个吧。
我们可以建立一个数组
假设
那么
我们可以知道,第
但是问题又来了:怎么找呢?
笔者一开始想到的是
int t=(1<<22),pos=0;
int tot=0;
for(;t;t>>=1){
if(tot+c[t+pos]<x){
pos+=t;
tot+=c[pos];
}
}
return pos+1;
这是一种非常实用的方法,在许多题目里都可以用来减少一个
大家一定都明白了吧!
时间复杂度:
最后吐槽一点,按照我随性的代码风格,卡空间这种操作令我痛不欲生啊……
下面是完整代码(部分变量名可能与题解里的不同,大家看懂就好)
#include<bits/stdc++.h>
using namespace std;
template<typename T>void Read(T &x){
x=0;int f=1;char s=getchar();
while(!isdigit(s)){if(s=='-') f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
const int maxn=5*1e6,maxk=1e4,fbnq_size=45;
int c[maxn+10],p[100];
inline int lowbit(int x){
return x&-x;
}
void add(int x,int y){
for(;x<=maxn;x+=lowbit(x)) c[x]+=y;
}
int pri[maxn/10];
bitset<maxn+10> fac;
short d[maxn+10];
int siz=0;
void do_d(){
for(int i=2;i<=maxn;i++){
if(fac[i]==0){
pri[++siz]=i;
d[i]=1;
}
for(int j=1;j<=siz;j++){
if(i*pri[j]>maxn) break;
fac[i*pri[j]]=1;
d[i*pri[j]]=d[i]+1;
if(i%pri[j]==0) break;
}
}
}
vector<int> sum[maxk+10];
vector<int> num[maxk+10];
int find_num(int x){
int t=(1<<22),pos=0;
int tot=0;
for(;t;t>>=1){
if(tot+c[t+pos]<x){
pos+=t;
tot+=c[pos];
}
}
return pos+1;
}
void pre_process(){
do_d();
p[1]=1;p[2]=2;
for(int i=3;i<=fbnq_size;i++) p[i]=p[i-1]+p[i-2];
for(int i=1;i<=maxn;i++){
add(i,1);
}
int tot=maxn;
for(int i=1;i<=maxk;i++){
for(int j=1;tot>=p[j]-j+1;j++){
int pos=find_num(p[j]-j+1);
add(pos,-1);tot--;
if(j==1) sum[i].push_back(d[pos]);
else{
int x=sum[i][j-2]+d[pos];
sum[i].push_back(x);
}
num[i].push_back(pos);
}
}
}
int main(){
pre_process();
int T;
Read(T);
for(int t=1;t<=T;t++){
int n,k;
Read(n);Read(k);
if(num[k][0]>n) printf("%d\n",-1);
else{
int pos=upper_bound(num[k].begin() ,num[k].end() ,n)-num[k].begin() -1;
printf("%d\n",sum[k][pos]);
}
}
return 0;
}