AtomAlpaca
不难发现第 行有 个数, 第 行有 个数, 这两行共有 个数.
我们看数据范围:
也就是说
也就是说, 最多只会取相当于最后两行数量的数. 因为越向下、越向右的数字越大, 且可以从最右方开始遍历最后两行, 我们只需要考虑最后两行就行了.
考虑贪心, 从第 行第 个数, 也就是第 行最大的数开始, 随后选择第 行有 个数, 也就是第 行最大的数, 然后再回到第 行, 选择第 个数.
也就是说, 我们要在第 行和第 行反复横跳, 每次都选择这行最大的数, 这样就能使和最大化.
我们以样例一举例, 我们选择了 这条路径. 我们可以将其视作在第 行选了三个最大的, 在第 行选了两个最大的, 完成了选择 个数字的目标.
我们推广一下, 任何这种问题都可以视作在第 行选 个最大的, 在第 行选 个最大的.
当选择个数为偶数时, 显然
当选择个数为奇数时, 因为第 行的数字总比第 行的数字大, 所以
第 行第 个数的值为:
显然第 行第 个数的值为:
第 行取 个最大的数, 它们的和为:
我们只需要分别将第 行与第 行的情况代入求和就行了.
这里注意 最大能达到 , 因此需要使用 long long 存储, 并且运算过程中还要取模. 这也是最后一步没有完全乘开的原因, 会爆 long long.
#include <iostream>
using std::cin;
using std::cout;
const long long MOD = 1e9 + 7;
long long T, N, K;
long long getNum(long long n, long long x)
{
return (x * ((n * (n + 1)) / 2 % MOD) - x * (x - 1) / 2 % MOD);
}
int main(int argc, char const *argv[])
{
cin.tie(NULL);
cout.tie(NULL);
std::ios::sync_with_stdio(false);
cin >> T;
while (T--)
{
cin >> N >> K;
cout << getNum(N - 1, K / 2) + getNum(N, (K - (K / 2))) % MOD << '\n';
}
return 0;
}