洛谷P5686 [CSP-S2019 江西] 和积和 题解

大佬们都很牛,本蒟蒻在此提供一种极其复杂的解法。

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=1nr=ln[(sarsal1)(sbrsbl1)]

把这个式子分配一下,得

∑ 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=1nr=ln[sarsbrsarsbl1sal1sbr+sal1sbl1]

如果把第二层求和代入进去,得

∑ 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=1n[r=ln[sarsbr]r=1nsarsbl1r=1nsbrsal1+sal1sbl1(nl+1)]

这就简单了。

  • 等式第一项,处理前缀和数组 s a i ⋅ s b i sa_i \cdot sb_i saisbi 的前缀和 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
!~~ 还请大家多多支持!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值