1 条题解

  • 0
    @ 2025-8-24 22:33:39

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar ycw123
    逆风如解意,容易莫摧残

    搬运于2025-08-24 22:33:39,当前版本为作者最后更新于2021-09-03 20:56:54,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题目大意

    已知 kk 和递推式

    f0=inf,fi=min{jfij>k×j}f_0=\inf,f_i=\min\{j|f_{i-j}>k\times j\}

    给定 nn,求 fnf_n

    solution

    对于 10% 的数据

    读懂题意,暴力模拟即可。

    对于 30% 的数据

    容易发现可以用单调栈优化,做到 O(n)O(n) 的复杂度。

    当 k=1 时

    打表可以发现答案为 lowbit(n)lowbit(n)

    当 k=2 时

    打表可以发现与斐波那契序列有关,也即接下来所说的序列 aa 为斐波那契序列去掉第一个 11

    对于全部数据

    假设存在正整数序列 aa 满足:

    对于任意一个正整数 nn,有且仅有一个序列 pp 满足

    1p1<p2<<pm1\le p_1 < p_2 < \cdots <p_m i=1mapi=n\sum_{i=1}^m{a_{p_i}}=n 1i<m,k×api<api+1\forall 1 \le i < m, k \times a_{p_i}<a_{p_{i+1}}

    注意pi+1p_{i+1},而不是 pi+1p_i+1。 我们称 ap1,ap2,...,apma_{p_1}, a_{p_2},...,a_{p_m} nn 的分解。 下面我们来证明对于正整数 nn,和任意满足条件的 aa,答案就是 ap1a_{p_1}

    该定理的简要证明

    首先我们发现 k,a1=1\forall k, a_1=1,也就是 f1=a1=1f_1=a_1=1

    然后进行归纳证明。对于正整数 i>1i>1,容易发现我们有

    fiap1=ap2f_{i-a_{p_1}}=a_{p_2}

    因为 iap1i-a_{p_1} 的分解为 nn 的分解去掉第一个数,即 ap2,ap3,...,apma_{p_2},a_{p_3},...,a_{p_m},而我们又有 k×ap1<ap2k \times a_{p_1}<a_{p_2},故 j=ap1j=a_{p_1} 时满足条件。

    接下来我们要证明对于所有 1j<ap11 \le j < a_{p_1} 均不满足条件 fij>k×jf_{i-j}>k\times j

    采用反证法,若 fij>k×jf_{i-j}>k\times j 成立,则 k×j<(ijk \times j < ( i-j 的分解的最小的数)),那么显然我们可以把 jj 的分解接上 iji-j 的分解形成一个新的分解,并且这个分解的不同于 ii 原来的分解,这样 ii 就有超过一个分解个数,与条件矛盾,所以 fi=ap1f_i=a_{p_1} 成立,证毕。

    序列的构造和证明

    不妨设 a0=0a_0=0,设序列 bib_i 表示 能用 a1,a2,...,aia_1,a_2,...,a_i 可以表示出来的最大整数,令 ai+1=bi+1a_{i+1}=b_i+1 即可。

    现在我们要证明这样构造的序列是满足条件的。

    我们还是采用归纳证明,容易发现 1=a1=11=a_1=1 满足条件。

    那么对于一个正整数 n>1n>1,我们设 m=max{mamn}m=\max\{m\mid a_m\le n\},根据序列的构造方法,我们容易证明 nn 的分解的最大数一定是 ama_m,那么 nn 的分解 =nam=n-a_m 的分解再添上一个 ama_m

    关于程序实现

    容易发现以及证明 bi=max{bjaj×k<i}+aib_i=\max\{b_j|a_j \times k<i\}+a_i

    我们只需要双指针一下,枚举当前的 i,j

    Code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3e7;
    long long n,a[N],b[N];
    int k,i=0,j=0;
    int main(){
        cin>>n>>k;
        while(a[i]<n) {
            ++i;
            a[i]=b[i-1]+1;
            while(j<i-1 && a[j+1]*k < a[i]) j++;
            b[i]=b[j]+a[i];
        }
        for(;i;--i)
            if(n-a[i]>0) n-=a[i];
            else if(n-a[i]==0) break;
        printf("%lld",a[i]);
        return 0;
    }
    
    • 1

    信息

    ID
    7088
    时间
    1000ms
    内存
    500MiB
    难度
    6
    标签
    (无)
    递交数
    0
    已通过
    0
    上传者