问题描述:
小蓝发现了一个有趣的数列,这个数列的前几项如下:
1,1,2,1,2,3,1,2,3,4,…
小蓝发现,这个数列前1项是整数1,接下来2项是整数1至2,接下来3项是整数1至3,接
下来4项是整数1至4,依次类推。
小蓝想知道,这个数列中,连续一段的和是多少。
输入格式
输入的第一行包含一个整数T,表示询问的个数。
接下来T行,每行包含一组询问,其中第i行包含两个整数和r:,表示询问数列中第l:个数
到第r:个数的和。
* 输出格式
输出T行,每行包含一个整数表示对应询问的答案。
package lanqiao;
/*问题描述
小蓝发现了一个有趣的数列,这个数列的前几项如下:
1,1,2,1,2,3,1,2,3,4,…
小蓝发现,这个数列前1项是整数1,接下来2项是整数1至2,接下来3项是整数1至3,接
下来4项是整数1至4,依次类推。
小蓝想知道,这个数列中,连续一段的和是多少。
输入格式
输入的第一行包含一个整数T,表示询问的个数。
接下来T行,每行包含一组询问,其中第i行包含两个整数和r:,表示询问数列中第l:个数
到第r:个数的和。
* 输出格式
输出T行,每行包含一个整数表示对应询问的答案。
*
*/
import java.util.Scanner;
/**
* 思路 第一项 1 第几项
思路分析:先二分查找l和r分别会出现在什么位置,然后计算l-r之间的数据sum,
但是,由于数据量过大,for循环求和(for(ll i=l+2;i<=r;i++) sum+=(i*(i+1)/2);)
必定超时,数列的通项公式n*(n+1)/2,故前n项和为n(n+1)(n+2)/6;
*
* @author LHL
*
*/
public class _123_21B_5_29 {
// 1414214
static long map[] = new long[1414220];// 前缀和数组
static long index[] = new long[1414220];// 索引数组
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
init();
int t = sc.nextInt();
for (int i = 0; i < t; i++) {
long l = sc.nextLong(), r = sc.nextLong();
System.out.println(getSum(r) - getSum(l - 1));// 输出流加快速度(男人不能太快)
}
}
static void init() {// 初始化
for (int i = 1, p = 1; i < index.length; i++, p++) {
index[i] = index[i - 1] + p;// 小学数列
map[i] = map[i - 1] + sum(i);// 利用小学公式算出前缀和
}
}
static long getSum(long tar) {
if (tar == 0)
return 0l;
int row = binarySearch(tar);
long n = tar - index[row];// n为 tar 在row+1行的位置
return ((1 + n) * n >> 1) + map[row];
}
static int binarySearch(long tar) {// 二分搜索
int l = 0, r = 1414219, mid;
while (l <= r) {
mid = l + ((r - l) >> 1);
if (index[mid] < tar)
l = mid + 1;
else
r = mid - 1;
}
return l - 1;// 直接上一行
}
static long sum(long x) {// 小学公式
return x * (x + 1) / 2;
}
}