题目
题目链接:https://codeforces.com/problemset/problem/1558/B
你有一个正整数 (n),当 (n>1) 时,可以选择以下两种操作:
- 选择一个在 ([1,n)) 范围内的整数 (x),让 (n) 减去 (x)。
- 选择一个在 ([2,n]) 范围内的整数 (x),让 (n) 除以 (x) 并下取整。
求有多少种方案使得 (n) 变为 (1)。答案对 (m) 取模。
(nleq 4 imes 10^6),(m) 是质数。(simplified version:(nleq 2 imes 10^5))。
时限 6s。
思路
设 (f[i]) 表示变到 (i) 的方案数。simplified version 的话由于 (lfloorfrac{i}{j}
floor) 只有 (O(sqrt{i})) 种取值,直接整除分块就可以了。时间复杂度 (O(nsqrt n))。
而这道题的话就不考虑刷表,考虑如何转移到 (f[i])。如果一个数除以 (j) 后下去整等于 (i),那么这个数的范围显然是 ([ij,(i+1)j-1])。那么直接枚举 (j),记一下后缀和即可。
时间复杂度 (O(sum_{i=1}^n lfloorfrac{n}{i}
floor)=O(nlog n))。
代码
/*
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native")
*/
#include <bits/stdc++.h>
#define YES printf("YES
")
#define Yes printf("Yes
")
#define NO printf("NO
")
#define No printf("No
")
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=4000010;
int Q,n,MOD,f[N],g[N];
int main()
{
//cerr<<(sizeof(f)+sizeof(g))/1024/1024<<"
";
scanf("%d%d",&n,&MOD);
f[n]=g[n]=1;
for (int i=n-1;i>=1;i--)
{
f[i]=g[i+1];
for (int j=2;i*j<=n;j++)
{
int l=i*j,r=min(n,(i+1)*j-1);
if (l>r) continue;
f[i]=((f[i]+g[l])%MOD-g[r+1])%MOD;
}
g[i]=(g[i+1]+f[i])%MOD;
}
cout<<(f[1]%MOD+MOD)%MOD;
return 0;
}