蓝桥杯 十二届 决赛 C/C++ 大学B组 F:123

时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分
【问题描述】
小蓝发现了一个有趣的数列,这个数列的前几项如下:
1, 1, 2, 1, 2, 3, 1, 2, 3, 4, …
小蓝发现,这个数列前 1 项是整数 1,接下来 2 项是整数 1 至 2,接下来
3 项是整数 1 至 3,接下来 4 项是整数 1 至 4,依次类推。
小蓝想知道,这个数列中,连续一段的和是多少。
【输入格式】
输入的第一行包含一个整数 T,表示询问的个数。
接下来 T 行,每行包含一组询问,其中第 i 行包含两个整数 l i 和 r i ,表示
询问数列中第 l i 个数到第 r i 个数的和。
【输出格式】
输出 T 行,每行包含一个整数表示对应询问的答案。

【样例输入】
3
1 1
1 3
5 8

【样例输出】
1
4
8

【评测用例规模与约定】
对于 10% 的评测用例,1 ≤ T ≤ 30, 1 ≤ l i ≤ r i ≤ 100。
对于 20% 的评测用例,1 ≤ T ≤ 100, 1 ≤ l i ≤ r i ≤ 1000。
对于 40% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ l i ≤ r i ≤ 10 6 。
对于 70% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ l i ≤ r i ≤ 10 9 。
对于 80% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ l i ≤ r i ≤ 10 12 。
对于 90% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ l i ≤ r i ≤ 10 12 。
对于所有评测用例,1 ≤ T ≤ 100000, 1 ≤ l i ≤ r i ≤ 10 12 。


解题思路:将子串分组,如1为一组,12为一组,123为一组.......

分组后找到大于左边界的最小的一组 

while(j<l)
        {
            count++;
            j += count;//循环结束时,j必定大于左边界
            flag[count] = j;
        }

在段代码中,count表示该组的项数,j用来记录总项数和各组的和

例如count为3时表示当前组为1,2,3,共有1,1,2,1,2,3,六项,因此j为6,该组的和为1+2+3=6;

找到左边界所在的组后要把该组中,左边界及左边界以后的值保存起来

while(j<l)
        {
            count++;
            j += count;//循环结束时,j必定大于左边界
        }
        ans+=((1+count)*count)/2-(count-(j-l))*(count-(j-l)-1)/2;//将左侧数值记录

ans表示区间和,j-l为这组有几个值应该被记入区间和,该组的项数count减掉(j-l)再减1(左边界)就是不应该被记入区间和的项的项数,然后用等差求和公式计算一波。

对右边界的操作和左边界差不多

while(j+count+1<r)//循环结束时j小于右边界
        {
            count++;
            j += count;
            ans += j;
        }

这里ans+=j就是把在区间内的值全保存起来

end[k++] = ans + (1+r - j)*(r-j)/2; //把右侧没加上的全加上

这里就是把右边多计算的删掉

全部代码:

// #include<iostream>
// #include<algorithm>
#include<bits/stdc++.h>
typedef unsigned long long ull;
using namespace std;
int main()
{
    ull T, l, r;
    cin >> T;
    ull end[T] = {0};
    int k = 0;
    for (long i = 0; i < T;i++)
    {
        cin >> l >> r;
        ull j = 1,count=1;
        ull ans = 0;//记录区间和
        while(j<l)
        {
            count++;
            j += count;//循环结束时,j必定大于左边界
        }
        ans+=((1+count)*count)/2-(count-(j-l))*(count-(j-l)-1)/2;//将左侧数值记录
        while(j+count+1<r)//循环结束时j必定大于右边界
        {
            count++;
            j += count;
            ans += j;
        }
        end[k++] = ans + (1+r - j)*(r-j)/2; //把右侧没加上的全加上
    }
    for (int i = 0; i < T;i++)
        cout << end[i] << endl;
    system("pause");
    return 0;
}

//1 5   999999              471699286
//1 1   10000000            14909805186
//1 1   100000000           471431361746
//1 1   1000000000          14907499131460
//1 1   10000000000         471408289857935
//1 1   100000000000        14907161212015550
//1 1   1000000000000       471404962308294300

注意!

代码不保熟,只是单纯的发一下自己的想法
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值