P5686 和积和

写在前面

数学题接触的少,卡了半下午,愣是没想到直接往前缀和上考虑,按元素强推莽了半下午

Description

直接跳转到题目去啊

Solution

\(A,B\) 的前缀和序列分别为 \(sA,sB\)

那么随便找一组数据,就会发现答案可以由一坨下标相等的前缀和积和减去另外一坨下标不等的前缀和积和。

而且下标相等的部分正好是对于每个下标 \(i\)\(sA_i sB_i\) 都被计算了 \(n\) 次。

那么加的部分就很好处理了。即:

\[n \bullet \sum_{i=1}^{n}sA_i sB_i \]

减的部分不难看出是 \(sA\)\(sB\) 所有的一一对应且下标不同的组合。

所以对于每个 \(sA_i\),只需要让它去乘以所有的下标 \(j<i\)\(sB_j\),并将结果求和。

对于每个 \(sb_i\) 亦然。

提公因式之后,由于计算过程是静态的,这个过程显然可以通过再预处理出一个超级前缀和 \(SA,Sb\),即前缀和的前缀和来优化实现。

那么这部分的计算式即为:

\[\sum_{i=2}^{n}sA_i SB_{i-1} + sB_i SA_{i-1} \]

总的式子即为:

\[n \cdot \sum_{i=1}^{n}sA_i sB_i - \sum_{i=2}^{n}sA_i SB_{i-1} + sB_i SA_{i-1} \]

根据计算式易知时间复杂度为 \(\Theta(n)\)

code:

#include<bits/stdc++.h>

#define LL long long

using namespace std;
const int Maxn = 5e5 + 5;
const int mod = 1e9 + 7;

inline LL read() {
  LL f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}

LL n, t;
LL a[Maxn], b[Maxn];
LL Sa[Maxn], Sb[Maxn];
LL sa[Maxn], sb[Maxn];
LL ans;

int main()
{
	n = read();
	t = (n % mod + 1 % mod) % mod;
	for(register int i = 1; i <= n; ++i) a[i] = read() % mod;
	for(register int i = 1; i <= n; ++i) b[i] = read() % mod;
	for(register int i = 1; i <= n; ++i)
	{
		sa[i] = ((sa[i - 1] % mod) + (a[i] % mod)) % mod;
		sb[i] = ((sb[i - 1] % mod) + (b[i] % mod)) % mod;
		Sa[i] = ((Sa[i - 1] % mod) + (sa[i] % mod)) % mod;
		Sb[i] = ((Sb[i - 1] % mod) + (sb[i] % mod)) % mod;
		ans = ((ans % mod) + (((((sa[i] % mod) * (sb[i] % mod)) % mod) * (n % mod)) % mod)) % mod;
	}
	for(register int i = 2; i <= n; ++i)
	{
		ans = ((ans % mod) - (((sa[i] % mod) * (Sb[i - 1] % mod)) % mod) + mod) % mod;
		ans = ((ans % mod) - (((sb[i] % mod) * (Sa[i - 1] % mod)) % mod) + mod) % mod;
	}
	printf("%lld", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/zimujun/p/13622543.html