洛谷 P8762 [蓝桥杯 2021 国 ABC] 123


题在洛谷里面洛谷居然没有题解,让我硬生生错了好几次! 

很明显这个序列是有规律的

    for(ll i=1;i<MAXN;++i)//a[i]为第i项和
        a[i]=a[i-1]+i;
    for(ll i=1;i<MAXN;++i)//s[i]为前i项之和(前缀和)
        s[i]=s[i-1]+a[i];

同时每一项的和又代表一共有多少个数,比如

{1},{1,2},{1,2,3}

sum{1,2,3}=6 刚好一共是6个数
输入的li和ri是给定数的位置,问[li,ri]的区间和是多少
所以考虑使用前缀和

所以我们要找到所给数的位置,这样就可以利用前缀和快速求出答案。

毫无疑问可以用
    int i;
    for(i=1;i<=MAXN;i++){
        if(a[i]>li)break;
    }
    i--;
来找出li的位置,必定是小于a[i]的
      
假设最后一部分li的落点{1,2,3,4,5,...,n}在5上面
                   i         li

那么答案就是s[i]+a[li-a[i]]

一个for找的太慢了,发现是线性,所以用二分来定位置

inline ll query(ll i){
    //确定第几项开始
    ll l=0,r=MAXN;
    while(l<r-1){
        ll mid=(l+r)>>1;
        if(a[mid]<=i)//位置太小了,往后找
            l=mid;
        else//往前
            r=mid;
    }
    return s[l]+a[i-a[l]];
}

因为内部没有+1,所以l==r-1就是最终我们需要的位置

AC代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const ll M=1414215;
int t;
ll l1,r1,a[M],s[M];
ll questsum(ll i){
    ll l=0,r=M+1;
    while((l+1)!=r){
        ll mid=(l+r)>>1;
        if(a[mid]<=i){
            l=mid;
        }else{
            r=mid;
        }
    }
    return(s[l]+a[i-a[l]]);
}

int main(){
    scanf("%d",&t);
    for(ll i=1;i<=M-1;i++){
        a[i]=a[i-1]+i;
    }
    for(ll i=1;i<=M-1;i++){
        s[i]=a[i]+s[i-1];
    }
    for(int i=1;i<=t;i++){
        scanf("%lld%lld",&l1,&r1);
        printf("%lld\n",questsum(r1)-questsum(l1-1));
    }
    return 0;
}

个人感觉手打二分还是比较麻烦的,一要考虑是否要内部要不要+1,循环内是l<r还是l<r-1之类的。

不如直接STL的lower_bound,虽然会慢上不少。

手写的话最好自行画图,保证代码正确性

存在l,m,r三者取值的问题就是因为二分的序列可能是一块区间都是正确的,取左端点和右端点就会有区别。 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值