D2. Up the Strip (Codeforces Round #740 (Div. 2))
D2. Up the Strip
https://codeforces.com/contest/1561/problem/D2
题目描述
有一个从 \(1\)到 \(n\)的单元格,你需要从 \(n\)走到 \(1\),当你处于单元格 \(x(1 <= x <= n)\)时,你有两种选择,
选择一个 \(y(1 <= y <= x-1)\),移动到 \(x-y\)的位置
选择一个 \(y(2 <= y <= x)\),移动到 \(\lfloor \frac ab \rfloor\)的位置
输出不同的方案数。
解题思路
首先这是一道动态规划题,同时可以想到 \(O(n^2)\)的转移方程: \(f(x) = \sum_{y=1}^{x-1}f(x-y) + \sum_{z=2}^xf(\lfloor \frac xz \rfloor)\)
前面部分的求和可以使用前缀和优化成 \(O(n)\)。
后面的部分比较麻烦,首先可以想到整除分块,但是 \(O(n\sqrt n)\)复杂度对于 \(D2\)来说还是无法接受,
所以需要一个 \(O(nlog n)\)的算法,于是想到枚举倍数,即从 \(1\)到 \(n\)扫一遍,当考虑第 \(i\)位时,我们枚举 \(i\)的倍数 \({[2i,\lfloor \frac ni \rfloor * i]}\),即对于 \(j\)来说,如果 \(i*j <= n\),那么对于 \([i*j, (i+1) *j - 1]\)中的数除以 \(j\)答案都是 \(i\),所以需要用 \(f(i)\)来转移,所以我么只需要枚举 \(j(2 <= j <= \lfloor \frac ni \rfloor)\)即可,使用差分的思想在 \(i*j\)处加上 \(f(i)\)的贡献,在 \((i+1)*j\)处减去 \(f(i)\)的贡献,当处理到 \(x\)位置时需要差分数组求出 \(1 - x\)位置的前缀和即是后面部分的结果。
Code
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int N = 4e6 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
ll dp[N],sum,ans[N],m;
void solve(){
int n;
cin >> n >> m;
dp[1] = 1;
sum = 0;
for(int i = 1; i <= n; i++){
ans[i] += ans[i-1];
ans[i] %= m;
dp[i] += (sum + ans[i]) % m;
for(ll j = 2; j * i <= n; j++){
ans[i*j] += dp[i];
ans[i*j] %= m;
ans[min(i*j+j,n+1ll)] += (m - dp[i]) % m;
ans[min(i*j+j,n+1ll)] %= m;
}
sum = (sum + dp[i]) % m;
}
cout << dp[n] << "\n";
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
qc;
int T = 1;
//cin >> T;
while(T--){
solve();
}
return 0;
}

浙公网安备 33010602011771号