曲师大2023大一新生排位赛-A.SUM题解

ldoj平台:竞赛3191 | 程序设计在线评测平台 (ldu.edu.cn)

A.Sum

给定 x ,y , 求\sum_{i = 1}^{y}\left \lfloor \frac{min(x,i^2+i+1)}{i+1} \right \rfloor的值 .

 其中 ,\left \lfloor \frac{a}{b} \right \rfloor代表向下取整

输入描述

第一行一个正整数 T(1≤T≤100), 表示测试用例的数目。每个测试用例占一行。
接下来的 T 行,每行两个正整数 x,y (1\leq T \leq 10^9)

输出描述

输出共 T 行,每个一个正整数,表示答案。

样例
输入:

9
3 4
2 100
4 3
50 3
12 4
69 420
12345 6789
123456 789
12345678 9

输出:

2
1
3
6
8
148
53494
161259
45

思路:

很容易知道i^2+i+1在大于0时间是单调递增的,所以在某点之前取i^2+i+1,在某点之后取x,这个点很容易求出来(循环跑或者公式都可以),前段计算时间就变成了\frac{i^2 + i + 1}{i + 1}, 化简变成了i+\frac{1}{i+1},因为下取整,所以后边的分数就没有了,前半段就变成了前n项和。
后半段可以通过分段计算,第一段的值就是\frac{x}{a+1}(a就是刚才求出的分割点),根据这个值,可以求出值为\frac{x}{a+1}的区间的长度,从这个值开始,后面的值就是前一个值-1,这样循环求就能求出来

部分代码:

void solve(){
    ll x,y;
    ll sum = 0;
    cin >> x >> y;
    ll a = 0;
    for(int i = 1;;i++){//求解分割点
        if(x <= i * i + i + 1){
            a = i - 1;
            break;
        }
    }
    if(y < a){//如果说泡不到分割点,就直接计算前半段输出
        sum = y * (y + 1) / 2;
        cout << sum << "\n";
        return ;
    }
    sum = a * (a + 1) / 2;
    a++;
    ll l = a;
    ll shang = x / (a + 1);
    if(shang == 0){//很关键,判断以下是否为0,否则后边计算区间右端点时间就会挂
        cout << sum << "\n";
        return;
    }
    ll r = x / shang - 1;
    while(1){
        if(y <= r){//算到边界就可以,超多的算就加多了
            sum += (y - l + 1) * shang;
            break;
        }
        sum += (r - l + 1) * shang;
        shang--;
        if(shang == 0)break;//如果这个值为0了,后面就没必要计算了,后面的贡献
        l = r + 1;
        r = x / shang - 1;
    }
    cout << sum << "\n";
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值