大佬们都很牛,本蒟蒻在此提供一种极其复杂的解法。
Part 1
首先,大家都能看出,前缀和可以将时间复杂度优化至 O ( n 2 ) O(n^2) O(n2),预测 70 p t s 70pts 70pts。
Part 2
考虑对前缀和进行优化。
∑ l = 1 n ∑ r = l n [ ( s a r − s a l − 1 ) ⋅ ( s b r − s b l − 1 ) ] \sum_{l = 1}^{n}\sum_{r = l}^{n}\left[(sa_r - sa_{l - 1}) \cdot (sb_r - sb_{l - 1})\right] l=1∑nr=l∑n[(sar−sal−1)⋅(sbr−sbl−1)]
把这个式子分配一下,得
∑ l = 1 n ∑ r = l n [ s a r ⋅ s b r − s a r ⋅ s b l − 1 − s a l − 1 ⋅ s b r + s a l − 1 ⋅ s b l − 1 ] \sum_{l = 1}^{n}\sum_{r = l}^{n}[sa_r \cdot sb_r - sa_r \cdot sb_{l - 1} - sa_{l - 1} \cdot sb_r + sa_{l - 1} \cdot sb_{l - 1}] l=1∑nr=l∑n[sar⋅sbr−sar⋅sbl−1−sal−1⋅sbr+sal−1⋅sbl−1]
如果把第二层求和代入进去,得
∑ l = 1 n [ ∑ r = l n [ s a r ⋅ s b r ] − ∑ r = 1 n s a r ⋅ s b l − 1 − ∑ r = 1 n s b r ⋅ s a l − 1 + s a l − 1 ⋅ s b l − 1 ⋅ ( n − l + 1 ) ] \sum_{l = 1}^n[\sum_{r = l}^n [sa_r \cdot sb_r] - \sum_{r = 1}^n sa_r \cdot sb_{l - 1} - \sum_{r = 1}^n sb_r \cdot sa_{l - 1} + sa_{l - 1} \cdot sb_{l - 1} \cdot (n - l + 1)] l=1∑n[r=l∑n[sar⋅sbr]−r=1∑nsar⋅sbl−1−r=1∑nsbr⋅sal−1+sal−1⋅sbl−1⋅(n−l+1)]
这就简单了。
-
等式第一项,处理前缀和数组 s a i ⋅ s b i sa_i \cdot sb_i sai⋅sbi 的前缀和 m s a b i msab_i msabi,单项时间复杂度优化至 O ( n ) O(n) O(n);
-
等式第二项和第三项,分别处理前缀和数组 s a i , s b i sa_i,sb_i sai,sbi 的前缀和 s s a i , s s b i ssa_i,ssb_i ssai,ssbi,单项时间复杂度同样优化至 O ( n ) O(n) O(n);
-
等式第四项,时间复杂度 O ( n ) O(n) O(n);
-
总时间复杂度 O ( n ) O(n) O(n),可以 100 p t s 100pts 100pts 了!
注意
为避免溢出,需要不断取模,不能放过一个细节,否则你会万劫不复……
(我写这道题时就是这个原因一直没过!现在过了,真的坑)
AC Code:
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const ll MOD = 1e9 + 7;
ll a[5000001], b[5000001], sa[5000001], sb[5000001], ssa[5000001], ssb[5000001], msab[5000001], n;
/*数组分别是:a数组,b数组,前缀和数组sa、sb,前缀和的前缀和数组ssa、ssb,前缀和数组的积的前缀和数组msab*/
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i], sa[i] = (a[i] + sa[i - 1]) % MOD, ssa[i] = (sa[i] + ssa[i - 1]) % MOD;
for (int i = 1; i <= n; i++)
cin >> b[i], sb[i] = (b[i] + sb[i - 1]) % MOD, ssb[i] = (sb[i] + ssb[i - 1]) % MOD;
for (int i = 1; i <= n; i++)
msab[i] = (sa[i] * sb[i] % MOD + msab[i - 1]) % MOD;
ll ans = 0;
for (int i = 1; i <= n; i++)
ans += ((((((msab[n] - msab[i - 1]) % MOD + MOD) % MOD //第一项
+ (sa[i - 1] * sb[i - 1]) % MOD * (n - i + 1) % MOD //第四项
- (ssa[n] - ssa[i - 1]) * sb[i - 1]) % MOD + MOD) % MOD //第二项
- (ssb[n] - ssb[i - 1]) * sa[i - 1]) % MOD + MOD) % MOD; //第三项
cout << ans % MOD;
return 0;
}
我怎么也想不到会把前缀和数组取前缀和,把前缀和数组的积取前缀和! 然后我居然想到了?
这篇题解如果有写得不好的地方,欢迎指出,栓Q
!~~ 还请大家多多支持!