Codeforces Round #740 D1. Up the Strip (整数分块 + 前缀和)

题目链接

题意

你有两个个整数 n ( 2 ≤ n ≤ 2 ∗ 1 0 5 ) , m ( 1 0 8 < m < 1 0 9 ) n(2 \le n \le 2*10^5),m(10^8<m<10^9) n2n2105,m(108<m<109) m m m为素数

可以进行两种操作

  • 选择一个数 y ( 1 ≤ y < x ) y (1 \le y < x) y(1y<x),将 x x x 变为 x − y x-y xy
  • 选择一个数 z ( 2 ≤ z ≤ x ) z(2 \le z \le x) z(2zx),将 x x x变为 x / z x/z x/z

问有多少种方案数可以将整数 n n n变为 1 1 1,输出方案数对 m m m取模

思路

考虑 d p dp dp d p [ i ] dp[i] dp[i]表示整数 i i i变为 1 1 1的方案数

容易得出转移方程为 d p [ i ] = ∑ j = 1 i − 1 d p [ j ] + ∑ j = 2 i d p [ i / j ] dp[i] = \sum_{j=1}^{i-1}dp[j] + \sum_{j=2}^{i}dp[i/j] dp[i]=j=1i1dp[j]+j=2idp[i/j]

如果直接求解时间复杂度为 O ( n 2 ) O(n^2) O(n2),则需要优化

容易看出 ∑ j = 1 i − 1 d p [ j ] \sum_{j=1}^{i-1}dp[j] j=1i1dp[j]是一个前缀和,用一个变量 s u m sum sum存下即可

∑ j = 2 i d p [ i / j ] \sum_{j=2}^{i}dp[i/j] j=2idp[i/j] 中的 i / j ( 2 ≤ j ≤ i ) i/j(2 \le j \le i) i/j(2ji) 容易看出是整数分块

tips

下面是整数分块的解释

对于一个整数 n n n,容易知道存在一个区间 ∀ i ∈ [ l , r ] \forall i \in [l,r] i[l,r] n / i n/i n/i相等

整数分块就是告诉你如果已知 l l l,那么 r = n / ( n / l ) r = n / (n/l) r=n/(n/l),至于为什么?这里以后有时间了再补吧

for(int l = 1,r ; l <= n ; l = r + 1){
    r = n / (n / l);//区间[l,r]内 n/i的值都为n/l
}

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
    ll s = 0, w = 1;
    char ch = getchar();
    while (ch < 48 || ch > 57) {
        if (ch == '-') w = -1;
        ch = getchar();
    }
    while (ch >= 48 && ch <= 57)
        s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}
ll f[200005];
int main() {
    f[1] = 1;
    ll n = read(), mod = read();
    ll sum = 0;//sum为前缀和
    for (int i = 2; i <= n; ++i) {
        sum = (sum + f[i - 1]) % mod;
        f[i] = sum;
        for (int l = 2, r; l <= i; l = r + 1) {//因为n/i时i是从2开始
            r = i / (i / l);
            f[i] = (f[i] + (r - l + 1) * f[i / l]) % mod;
        }
    }
    cout << f[n] << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值