【CF1558B】Up the Strip
题目
题目链接:https://codeforces.com/problemset/problem/1558/B
你有一个正整数 \(n\),当 \(n>1\) 时,可以选择以下两种操作:
- 选择一个在 \([1,n)\) 范围内的整数 \(x\),让 \(n\) 减去 \(x\)。
- 选择一个在 \([2,n]\) 范围内的整数 \(x\),让 \(n\) 除以 \(x\) 并下取整。
求有多少种方案使得 \(n\) 变为 \(1\)。答案对 \(m\) 取模。
\(n\leq 4\times 10^6\),\(m\) 是质数。(simplified version:\(n\leq 2\times 10^5\))。
时限 6s。
思路
设 \(f[i]\) 表示变到 \(i\) 的方案数。simplified version 的话由于 \(\lfloor\frac{i}{j}\rfloor\) 只有 \(O(\sqrt{i})\) 种取值,直接整除分块就可以了。时间复杂度 \(O(n\sqrt n)\)。
而这道题的话就不考虑刷表,考虑如何转移到 \(f[i]\)。如果一个数除以 \(j\) 后下去整等于 \(i\),那么这个数的范围显然是 \([ij,(i+1)j-1]\)。那么直接枚举 \(j\),记一下后缀和即可。
时间复杂度 \(O(\sum_{i=1}^n \lfloor\frac{n}{i}\rfloor)=O(n\log 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\n")
#define Yes printf("Yes\n")
#define NO printf("NO\n")
#define No printf("No\n")
#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<<"\n\n";
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;
}