假定读者已经看过其他题解,这里提供一个较为清晰的Java代码思路。
import java.io.*;
import java.util.*;
public class Main {
// 对于本题来说,1e12内的数用本题的表示法,
// 分别按12,123,1234,12345...划分成1,2,3...行,可在1500000行 内划分完毕
static int N = 1500000;
static long a[] = new long[N], s[] = new long[N], row[] = new long[N];
static long left, right;
public static long work(){
// 在a[]中找到最小的大于等于left的值,同时也会找到left所在的二维行数
int l = 1, r = N - 1;
while(l < r){
int mid = l + r >> 1;
if(a[mid] >= left) r = mid;
else l = mid + 1;
}
long tl = a[r]; // 记录第tlidx行最后一位数的一维下标
int tlidx = r; // 记录left所在的行数
int ctlidx = tlidx - (int)(tl - left); // 记录left所在的列数
// 在a[]中找到最小的大于等于right的值,同时也会找到right所在的二维行数
l = 1; r = N - 1;
while(l < r){
int mid = l + r >> 1;
if(a[mid] >= right) r = mid;
else l = mid + 1;
}
long tr = a[r]; // 记录第tridx行最后一位数的一维下标
int tridx = r; // 记录right所在的行数
int ctridx = tridx - (int)(tr - right); // 记录right所在的列数
// 如果left和right在同一行,按前缀和a[]返回
long res = 0;
if(tridx - tlidx == 0) res = a[ctridx] - a[ctlidx - 1];
else {
// 如果二者不在同一行
res += s[tridx - 1] - s[tlidx]; // 先加它俩间所有行的和
res += a[tlidx] - a[ctlidx - 1]; // 再加left所在行从它到行尾所有数的和
res += a[ctridx]; // 再加right所在行从头到它所有数的和
}
return res;
}
public static void init(){
// a[i]即表示第i行的和,又表示第i行最后一个数对应一维的下标
for(int i = 1; i < N; i++) a[i] = a[i - 1] + i;
// s[i]表示前i行所有数的和
for(int i = 1; i < N; i++) s[i] = s[i - 1] + a[i];
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StreamTokenizer stmInput = new StreamTokenizer(br);
stmInput.nextToken();
int T = (int)stmInput.nval;
// 1.预处理a[], s[]
init();
while(T-- != 0){
stmInput.nextToken();
left = (long)stmInput.nval;
stmInput.nextToken();
right = (long)stmInput.nval;
bw.write(work() + "\n");
}
bw.flush();
bw.close();
br.close();
}
}