洛谷P1147 连续自然数和

本文介绍了求解连续自然数和的两种算法方法,包括前缀和的直接枚举解法及优化版本,以及利用等差数列求和公式的数学解法。通过具体代码实现展示了不同算法的效率对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目解法:
这题是个大水题,但是我们看到了两种解法:
法一①:考虑前缀和做法。
首先我们读题,发现其要求的是连续自然数和。连续自然数和,那么我们可以用前缀和来处理这些自然数的和。于是我们得到了一个显然的做法:(30pts)直接枚举。
Code:

#include<iostream>
#include<cstdio>
#define ll long long
#define maxn 2000001
using namespace std;
int m,s[maxn];
int main(){
    cin>>m;
    s[0]=0;
    for(int i=1;i<=m;i++){
        s[i]=s[i-1]+i;
    }
    for(int i=1;i<=m;i++){
        for(int j=1;j<i;j++){
            if(s[i]-s[j-1]==m){
                cout<<j<<" "<<i<<endl;
            }
        }
    }
    return 0;
}

法一②:考虑前缀和做法的优化版本。我们发现,在上一个阶段的代码上,我们可以对内层循环j做一些处理。比如,我们可以对这个j进行二分答案,得到一个j,然后从i到j,就是答案要求的区间。
Code:

#include<iostream>
#include<cstdio>
#define maxn 2000001
#define int long long
using namespace std;
int m,s[maxn];
int check(int ll,int rr,int k){
    if(ll>rr) return -1;
    int mid=(ll+rr)/2;
    if(s[mid]-k==m) return mid;
    else if(s[mid]-k>m) check(ll,mid-1,k);
    else if(s[mid]-k<m) check(mid+1,rr,k);
}
main(){
    cin>>m;
    s[0]=0;
    for(int i=1;i<=m;i++){
        s[i]=s[i-1]+i;
    }
    for(int i=1;i<=m;i++){
    /*    for(int j=1;j<i;j++){
            if(s[i]-s[j-1]==m){
                cout<<j<<" "<<i<<endl;
            }
        }*/
        int k=check(i+1,m,s[i-1]);
        if(k!=-1) cout<<i<<" "<<k<<endl;
    }
    return 0;
}

法二:考虑数学方法。
在洛谷标签上,我们可以发现一个标签:数学,数论。那么这就代表了数学解法。观察这道题,我们发现,这些数字的差值都是一样的,这其实是个等差数列。
对于每个等差数列,我们有个等差数列求和公式,直接带入这个公式就可以A了这个题。
Code详见Luogu题解。

转载于:https://www.cnblogs.com/kenlig/p/9822590.html

### 计算带有前缀连续自然数 为了计算具有前缀连续自然数序列的求方法,可以通过以下方式理解: #### 前缀的概念 前缀是指在一个数组中,第 \( i \) 项的前缀表示从数组的第一项到第 \( i \) 项所有元素的总。对于自然数序列来说,假设有一个长度为 \( n \) 的自然数序列 \( a_1, a_2, ..., a_n \),其对应的前缀数组 \( S \) 定义如下: \[ S[i] = a_1 + a_2 + ... + a_i \] 因此,任意子区间 \( [l,r] \) (\( l \leq r \))内的元素可以用两个前缀相减得出: \[ sum(l, r) = S[r] - S[l-1] \] 其中,当 \( l=0 \) 时,定义 \( S[-1]=0 \)[^1]。 #### 连续自然数公式的推导 对于一个由第一个自然数 \( a \) 开始、共有 \( n \) 个连续自然数构成的序列而言,该序列的可以利用等差数列求公式获得: \[ Sum = \frac{n}{2}(a+(a+n-1))=\frac{(2*a+n-1)*n}{2} \]。 #### 使用前缀优化查询过程 通过预先构建好整个序列的前缀表,在面对多次询问某段区间的数值加总之类的问题时能够快速给出答案而无需重复累加操作,从而大大提高了效率。 ```python def prefix_sum(n): # 构建前缀列表 pre_sums = [0]*(n+1) for i in range(1,n+1): pre_sums[i] = pre_sums[i-1]+i return pre_sums def get_range_sum(pre_sums,l,r): """获取指定范围内[l,r]内所有数字的""" return pre_sums[r]-pre_sums[l-1] if __name__ == "__main__": N = int(input("请输入要处理的最大自然数N:")) pre_sums = prefix_sum(N) while True: try: L,R = map(int,input(f"输入想要知道哪一段闭合区间[{1},{N}]之间的(按Ctrl+C退出程序)").split()) if not (1<=L<=R<=N): raise ValueError() print(get_range_sum(pre_sums,L,R)) except KeyboardInterrupt: break except Exception as e: print('错误:',str(e),'.请重新尝试.') ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值