题意:有n阶楼梯,假设当前你处于第x阶,那么走到第[1,x-1]阶或第(lfloor dfrac{x}{k} floor(2le k le x))阶,问从n阶到1阶的方案数
即求递归式
[
m f(x)=sumlimits_{i=1}^{x-1}f(i)+sum_{i=2}^{x}f(lfloor dfrac{x}{i}
floor)
]
注意到前面(sum_{i=1}^{x-1}f(i))可以用前缀和O(1)计算,问题就是快速计算后面的式子
首先想到了整除分块,就完成了一个( m O(nsqrt n))的算法,可以通过Easy Version的数据
考虑如何更快地完成这个操作,正难则反,考虑每个f(x)往后的贡献
对于一个f(x),它能贡献到的位置v,满足存在一个k,使得
[lfloor dfrac{v}{k}
floor=x
]
化简就是
[kxle v < k(x+1)
]
就是说,枚举一个k,x能贡献到的是一个连续区间[kx,k(x+1))
这样的枚举是倍数型的,用差分+前缀和完成区间加(因为只会往后加)
复杂度有一个调和级数,为( m O(nlog n))
/*
* Author : GhostCai
* Expecto Patronum
*/
#include<bits/stdc++.h>
#define int long long
using namespace std;
void debug_out() { cerr << endl; }
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T){cerr << " " << H;debug_out(T...);}
void shit(){puts("NO");}
void good(){puts("YES");}
#define debug(...)
cerr << __LINE__ << " [" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#define pb push_back
#define fi first
#define se second
#define read(a,n) for(int i=1;i<=n;i++) a[i]=rd()
#define mem0(a) memset(a,0,sizeof(a))
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define eep(i,x) for(int i=head[x];i;i=e[i].next)
inline int rd(){
int ret=0,f=1;char c;
while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
while(isdigit(c))ret=ret*10+c-'0',c=getchar();
return ret*f;
}
#define pc putchar
#define space() pc(' ')
#define nextline() pc('
')
void pot(int x){if(!x)return;pot(x/10);pc('0'+x%10);}
void out(int x){if(!x)pc('0');if(x<0)pc('-'),x=-x;pot(x);}
const int MAXN = 5000006;
int n,MOD;
int f[MAXN],g[MAXN];
void solve(){
n=rd();MOD=rd();
f[1]=1;
rep(i,1,n){
(g[i]+=g[i-1])%=MOD;
(f[i]+=g[i])%=MOD;
(g[i+1]+=f[i])%=MOD;
(g[n+1]-=f[i])%=MOD;
for(int k=2;k*i<=n;k++){
int l=k*i,r=min(n+1,k*i+k);
g[l]=(g[l]+f[i])%MOD;
g[r]=(g[r]-f[i])%MOD;
}
}
f[n]%=MOD;
if(f[n]<0) f[n]+=MOD;
cout<<f[n]<<endl;
}
signed main(){
solve();
return 0;
}