G. Hits Different
G. Hits Different
In a carnival game, there is a huge pyramid of cans with $2023$ rows, numbered in a regular pattern as shown.
If can $9^2$ is hit initially, then all cans colored red in the picture above would fall.
You throw a ball at the pyramid, and it hits a single can with number $n^2$. This causes all cans that are stacked on top of this can to fall (that is, can $n^2$ falls, then the cans directly above $n^2$ fall, then the cans directly above those cans, and so on). For example, the picture above shows the cans that would fall if can $9^2$ is hit.
What is the sum of the numbers on all cans that fall? Recall that $n^2 = n \times n$.
Input
The first line contains an integer $t$ ($1 \leq t \leq 1000$) — the number of test cases.
The only line of each test case contains a single integer $n$ ($1 \leq n \leq 10^6$) — it means that the can you hit has label $n^2$.
Output
For each test case, output a single integer — the sum of the numbers on all cans that fall.
Please note, that the answer for some test cases won't fit into 32-bit integer type, so you should use at least 64-bit integer type in your programming language (like long long for C++). For all valid inputs, the answer will always fit into 64-bit integer type.
Example
input
10 9 1 2 3 4 5 6 10 1434 1000000
output
156 1 5 10 21 39 46 146 63145186 58116199242129511
Note
The first test case is pictured in the statement. The sum of the numbers that fall is $$1^2 + 2^2 + 3^2 + 5^2 + 6^2 + 9^2 = 1 + 4 + 9 + 25 + 36 + 81 = 156.$$
In the second test case, only the can labeled $1^2$ falls, so the answer is $1^2=1$.
In the third test case, the cans labeled $1^2$ and $2^2$ fall, so the answer is $1^2+2^2=1+4=5$.
In the fourth test case, the cans labeled $1^2$ and $3^2$ fall, so the answer is $1^2+3^2=1+9=10$.
In the fifth test case, the cans labeled $1^2$, $2^2$, and $4^2$ fall, so the answer is $1^2+2^2+4^2=1+4+16=21$.
解题思路
我们先对金字塔转换成矩阵的形式,结果如下图:
以上图为例子,如果选择$13$号的格子,此时对应的坐标为$(5,3)$(矩阵坐标系)。那么在金字塔中的上一行与$13$号格子有接触的是$8$号和$9$号格子,因此我们想到能不能利用$8$号和$9$号格子的结果来得到$13$号格子的结果呢?
定义$f(x)$表示从$x$号格子往上能走到的与其接触的格子的值之和。先给出结论,$f(13) = 13^2 + f(8) + f(9) - f(5)$。为什么要减去一个$f(5)$?这是因为从$8$号格子往上走会经过$4$号格子与$5$号格子,从$9$号格子往上走会经过$5$号格子与$6$号格子,因此$f(8)+f(9)$会有重复的部分,即$f(5)$,因此减去即可,有容斥原理的思想在里面。
递推公式知道了,那么接下来问题是我知道了$x$号格子,怎么得到上面的两个格子的编号呢?在矩阵的表示中可以发现第$i$行恰好有$i$个格子,因此如果知道当前$x$号格子的坐标为$(i,j)$,那么上面两个的坐标就是$(i-1,j)$和$(i-1,j-1)$,要减去的格子坐标是$(i-2,j-1)$。而已知坐标推格子编号还是很容易的,有公式$g(i,j) = \sum\limits_{k=1}^{i-1}{k}+j = \dfrac{i \cdot (i-1)}{2}+j$,因此递推公式就变成了$$f(g(i,j)) = {g^2}(i,j) + f(g(i-1,j)) + f(g(i-1,j-1)) - f(g(i-2,j-1))$$
可以发现每次用到的都是上一行的结果,因此我们可以从第$2$行开始往下递推(其中$f(1)=1$)。然后可能往上走有些格子并不存在,此时公式就有所变化了,如果对应的格子不存在那么直接不考虑该项结果即可,对应的细节处理见代码。
我们直接预处理出来$10^6$以内的$f(x)$,询问的时候直接查表,因此计算量为$O(10^6 + T)$。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 1e6 + 10; 7 8 LL f[N]; 9 10 void init() { 11 f[1] = 1; // 边界情况f(1)=1 12 for (int i = 2, k = 2; k < N; i++) { // 从第2行开始枚举,k是要枚举的格子编号 13 for (int j = 1; j <= i; j++, k++) { // 第i行最多有i列,即纵坐标不超过横坐标 14 f[k] = 1ll * k * k; 15 if (i - 1 > 0) { // 上一行(第i-1行)存在 16 if (j <= i - 1) f[k] += f[(i - 2) * (i - 1) / 2 + j]; // 列不超过行,存在格子(i-1,j) 17 if (j - 1 > 0) { // 首先必然有j-1 <= i-1,然后还要保证j-1 > 0,没有越界,才存在格子(i-1,j-1) 18 f[k] += f[(i - 2) * (i - 1) / 2 + j - 1]; 19 if (i - 2 > 0 && j - 1 <= i - 2) f[k] -= f[(i - 3) * (i - 2) / 2 + j - 1]; // 第i-2行存在且列不超过行(已经保证j-1 > 0了) 20 } 21 } 22 } 23 } 24 } 25 26 void solve() { 27 int n; 28 scanf("%d", &n); 29 printf("%lld\n", f[n]); 30 } 31 32 int main() { 33 init(); 34 int t; 35 scanf("%d", &t); 36 while (t--) { 37 solve(); 38 } 39 40 return 0; 41 }
参考资料
Codeforces Round 871 (Div. 4) A - H:https://zhuanlan.zhihu.com/p/627396904
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17381722.html