交叉乘
题目描述
上小学二年级的牛牛已经精通整数的加法和乘法。现在你要考考他。你先给出一个整数数组a ,然后每次你会给一对整数l,r(l≤r),牛牛需要算出
∑
i
=
l
r
\sum_{i=l}^r
∑i=lr
∑
j
=
i
+
1
r
\sum_{j=i+1}^r
∑j=i+1r
a
i
a_i
ai *
a
j
a_j
aj
的值。
但是牛牛算完之后你不能不确定他算得对不对,为了验证他的答案,你决定自己算一遍。
为了不输出过大的答案,假设答案为ans,请输出ans % 1,000,000,007。
题解
这道题是要我们遍历求和,看了数据范围以后发现,直接暴力一定会超时,因为我们做了很多重复的计算。
所以可以使用前缀和进行一波优化,使用新数组对前缀和存储。
其次我们要做的是对原式进行化简:
我们设 c u r [ i ] = cur[i]= cur[i]= ∑ k = 0 n a k \sum_{k=0}^n a_k ∑k=0nak 则可以将原式转换为:
∑ i = l r \sum_{i=l}^r ∑i=lr a i a_i ai ∗ * ∗ ( c u r [ r ] − c u r [ i ] ) (cur[r] - cur[i]) (cur[r]−cur[i])
由分配律可得到
∑ i = l r \sum_{i=l}^r ∑i=lr a i ∗ c u r [ r ] a_i * cur[r] ai∗cur[r] − - − ∑ i = l r \sum_{i=l}^r ∑i=lr a i ∗ c u r [ i ] a_i * cur[i] ai∗cur[i]
可以设 p [ i ] = p[i] = p[i]= ∑ k = 1 i \sum_{k=1}^i ∑k=1i a k ∗ c u r [ k ] a_k * cur[k] ak∗cur[k]
最后 原式可化简为
=
=
=
(
c
u
r
[
r
]
−
c
u
r
[
l
−
1
]
)
∗
c
u
r
[
r
]
−
(
p
[
r
]
−
p
[
l
−
1
]
)
(cur[r]-cur[l-1]) * cur[r] - (p[r] -p[l-1])
(cur[r]−cur[l−1])∗cur[r]−(p[r]−p[l−1])
代码
private long[] cur = new long[100100];
private long[] p = new long[100100];
private long[] k = new long[100100];
private long[] arr = new long[100100];
private long mod = 1000000007;
public int[] getSum (int[] a, int[] query) {
// write code here
int n = a.length;
int[] ans = new int[query.length/2];
int in = 0;
for(int i = 1;i <= n; i++) {
k[i] = a[i-1];
}
for(int i = 1;i <= n; i++) {
cur[i] = (cur[i-1] + k[i])%mod;
}
for (int i=1;i<=n;i++) {
arr[i]=k[i]*cur[i]%mod;
}
for (int i=1;i<=n;i++) {
p[i]=(p[i-1]+arr[i])%mod;
}
for(int i = 0;i < query.length; i+=2) {
int l = query[i], r = query[i+1];
long vv = ((cur[r] - cur [l-1]) * cur[r] % mod + mod - (p[r]-p[l-1]))%mod;
vv += mod;
vv %= mod;
ans[in++] = (int) vv;
}
return ans;
}