【CF1558B】Up the Strip
阿新 • • 發佈:2021-08-25
題目
題目連結: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; }