有 n n n个细胞,你初始在第 n n n细胞上,假设你当前在 x x x处,你每次可以进行如下两个操作:
( 1 ) (1) (1)选择 [ 1 , x − 1 ] [1,x-1] [1,x−1]内一个数 y y y,跳到第 x − y x-y x−y个细胞上。
( 2 ) (2) (2)选择 [ 2 , x ] [2,x] [2,x]之间的一个数 z z z,跳到 ⌊ x z ⌋ \left \lfloor \frac{x}{z} \right \rfloor ⌊zx⌋。
问你有多少种不同的方式到达 1 1 1号细胞。
2 ≤ n ≤ 4 e 6 2\le n\le 4e6 2≤n≤4e6
如果直接按照题意来设计状态, f [ i ] f[i] f[i]表示从 n n n到 i i i的方案数,那么第一个操作显然可以用一个变量打一个标记,第二个可以整除分块,将 i i i这个点的贡献分配给 ⌊ x z ⌋ \left \lfloor \frac{x}{z} \right \rfloor ⌊zx⌋。
复杂度 O ( n n ) O(n\sqrt n) O(nn)
这个只能通过简单版本,要通过这个题的话,显然需要优化,其实不难发现他与倍数有关。
我们考虑倒着来设计状态, f [ i ] f[i] f[i]表示从 i i i到 1 1 1的方案数,那么 f [ 1 ] = 1 f[1]=1 f[1]=1。
假设当前枚举的点是 k k k,考虑 ⌊ x z ⌋ = k \left \lfloor \frac{x}{z} \right \rfloor=k ⌊zx⌋=k这个式子,代表从 x x x点能到当前点 k k k,由于我们是倒着来的,那么也就是 k k k这个点可以转移到 x x x,考虑枚举 z z z,那么能转移到的区间就是 [ k ∗ z , k ∗ z + z − 1 ] [k*z,k*z+z-1] [k∗z,k∗z+z−1],通过枚举倍数让后打一个标记即可。
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
// Problem: D1. Up the Strip (simplified version)
// Contest: Codeforces - Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine))
// URL: https://codeforces.com/contest/1561/problem/D1
// Memory Limit: 128 MB
// Time Limit: 6000 ms
//
// Powered by CP Editor (https://cpeditor.org)
//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
#define lowbit(x) (x&(-x))
using namespace std;
//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N=5000010,INF=0x3f3f3f3f;
const double eps=1e-6;
int n,mod;
LL a[N],f[N];
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
scanf("%d%d",&n,&mod);
f[1]=1; LL add=0;
for(int i=1;i<=n;i++) {
a[i]+=a[i-1]; a[i]%=mod;
f[i]=add+f[i]+a[i]; f[i]%=mod;
add+=f[i]; add%=mod;
for(int j=2;1ll*j*i<=n;j++) {
int l=j*i,r=j*i+j-1; r=min(r,n+1);
a[l]+=f[i]; a[r+1]-=f[i];
a[l]%=mod; a[r+1]%=mod; a[r+1]+=mod; a[r+1]%=mod;
}
}
cout<<f[n]%mod<<endl;
return 0;
}
/*
1 2 3 4 5 6 7 8
1-> 2 3 4 5 6 7 8
2-> [4,5]
*/