link
a
n
s
=
∑
i
∑
j
∑
k
a
[
p
1
+
d
∗
i
+
j
]
×
a
[
p
2
+
d
∗
j
+
k
]
(
0
≤
i
,
j
,
k
<
d
)
ans=\sum_i \sum_j \sum_k a[p1+d*i+j]\times a[p2+d*j+k] (0\le i,j,k<d)
ans=∑i∑j∑ka[p1+d∗i+j]×a[p2+d∗j+k](0≤i,j,k<d)
因为这个 k k k 的加 1 1 1 只会导致每次后面那个数的下标加 1 1 1,考虑前缀和。
令 p r e [ i ] = ∑ a [ k ] ( k < = i ) pre[i]=\sum a[k](k<=i) pre[i]=∑a[k](k<=i)
∑ i ∑ j a [ p 1 + d ∗ i + j ] ∗ ( p r e [ p 2 + d ∗ j + d − 1 ] − p r e [ p 2 + d ∗ j − 1 ] ) \sum_i \sum_j a[p1+d*i+j]*(pre[p2+d*j+d-1]-pre[p2+d*j-1]) ∑i∑ja[p1+d∗i+j]∗(pre[p2+d∗j+d−1]−pre[p2+d∗j−1])
每次 i i i 的变化只会影响一个值,老套路,移一下求和。
∑ j ∗ ( p r e [ p 2 + d ∗ j + d − 1 ] − p r e [ p 2 + d ∗ j − 1 ] ) ∗ ∑ i a [ p 1 + d ∗ i + j ] \sum_j*(pre[p2+d*j+d-1]-pre[p2+d*j-1])*\sum_i a[p1+d*i+j] ∑j∗(pre[p2+d∗j+d−1]−pre[p2+d∗j−1])∗∑ia[p1+d∗i+j]
再考虑前缀和,引入 p r e [ i ] [ j ] pre[i][j] pre[i][j] 为公差为 j j j 的前缀和。
∑ j ∗ ( p r e [ p 2 + d ∗ j + d − 1 ] [ 1 ] − p r e [ p 2 + d ∗ j − 1 ] [ 1 ] ) ∗ ( p r e [ p 1 + d ∗ ( d − 1 ) + j ] [ d ] − p r e [ p 1 + j − d ] [ d ] ) \sum_j*(pre[p2+d*j+d-1][1]-pre[p2+d*j-1][1])*(pre[p1+d*(d-1)+j][d]-pre[p1+j-d][d]) ∑j∗(pre[p2+d∗j+d−1][1]−pre[p2+d∗j−1][1])∗(pre[p1+d∗(d−1)+j][d]−pre[p1+j−d][d])(注意这里是减 d d d,不是减 1 1 1,因为公差是 d d d)
然后算一算时间复杂度: O ( n ln n + n ∗ d ) \mathcal {O}(n\ln n+n*d) O(nlnn+n∗d),乍一看过不了,但题目保证 p r e pre pre 数组的下标不超过 n n n,即 d 2 ≤ n d^2\le n d2≤n 则 d ≤ n d\le \sqrt n d≤n。
所以时间复杂度为: O ( n ln n + n n ) \mathcal {O}(n\ln n+n\sqrt n) O(nlnn+nn)。有点卡常,需要把 p r e pre pre 数组小的一维放在前面,这样可以快一些。另外,对 2 32 2^{32} 232 取模,开个 u n s i g n e d i n t unsigned\ int unsigned int 就行了。
此题难点:1.想到用公差为 j j j 的前缀和表示。2.发现 d ≤ n d\le \sqrt n d≤n。3.卡常。
Code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <climits>
#define unsigned int
using namespace std;
const int MAXN = 2e5 + 5;
int n, m, a[MAXN], pre[450][MAXN], t, ans;
signed main() {
int d, p1, p2;
scanf("%u", &n); t = sqrt(n);
for(int i = 1; i <= n; i ++) scanf("%u", &a[i]);
for(int i = 1; i <= t; i ++) for(int j = 1; j <= n; j ++) pre[i][j] = (j - i >= 1 ? pre[i][j - i] : 0) + a[j];
scanf("%u", &m);
for(int i = 1; i <= m; i ++) {
scanf("%u%u%u", &d, &p1, &p2); ans = 0;
for(int j = 0; j < d; j ++) ans += (pre[1][p2 + d * j + d - 1] - pre[1][p2 + d * j - 1]) * (pre[d][p1 + d * (d - 1) + j] - (p1 + j - d >= 1 ? pre[d][p1 + j - d] : 0));
printf("%u\n", ans);
}
return 0;
}