P8762 [蓝桥杯 2021 国 ABC] 123题解

· · 题解

题目传送门

本题思路

本题要生成一个数列,例如:1,1,2,1,2,3,1,2,3,4,…,所以我们把这个数列看成一个二维数组:

1
1 2
1 2 3
1 2 3 4
……

那么我们假设这个二维数组有 n 行,那么数列长度 len 则为:

len = \cfrac{n+n^2}{2}

题目给出我们数组长度,逆推公式最大值可找到行数。

查找行数函数:

int max_len(int n){
    int l=1,r=n;
    while(l<=r){//二分查找
        int mid = (l+r)/2;
        if((mid*mid+mid)/2<n){
            l=mid+1;
        }else{
            r=mid-1;
        }
    }
    return r+1;
}

给定长度求和分为两块:

S_n = S_v + S_y

其中 S_v 为确定行数减一行总和,y 就等于 len-\cfrac{(x-1)+(x-1)^2}{2}

最后一行就是 1+2+...+y 求和。

S_y=\cfrac{(1+y) \times y}{2}=\cfrac{y+y^2}{2}

接下来求 S_n

S_n=S_{n-1}+S_h=S_{n-1}+\cfrac{n+n^2}{2} 预处理代码: ```cpp for(long long i=1;i<=1600001;i++)//预处理S_n sum[i]=sum[i-1]+(i+i*i)/2; ``` 接下来是求和函数,参数为数组的长度: ```cpp ll sum_len(ll len){ ll x=max_len(len);//最大行数 ll y=len-(x-1+(x-1)*(x-1))/2;//最后一行的长度 ll S_y=(y+y*y)/2;//最后一行的和 ll S_v=sum[x-1];//前面n行的和 return S_y+S_v;//总和 } ``` 最终代码如下: ```cpp #include <bits/stdc++.h> using namespace std; #define ll long long long long sum[1600010]={0}; int max_len(int n){ int l=1,r=n; while(l<=r){//二分查找 int mid = (l+r)/2; if((mid*mid+mid)/2<n){ l=mid+1; }else{ r=mid-1; } } return r+1; } ll sum_len(ll len){ ll x=max_len(len);//最大行数 ll y=len-(x-1+(x-1)*(x-1))/2;//最后一行的长度 ll S_y=(y+y*y)/2;//最后一行的和 ll S_v=sum[x-1];//前面n行的和 return S_y+S_v;//总和 } int main(){ //预处理S_n前n行和 for(long long i=1;i<=1600001;i++) sum[i]=sum[i-1]+(i+i*i)/2; int N; scanf("%d",&N); while(N--){ ll x,y; cin>>x>>y; cout<<sum_len(y)- sum_len(x-1)<<endl } return 0; } ```