CSP-SJX2019 和积和
Solution
考虑每个数 \(a_i\) 对答案的贡献:若 \(b\) 中存在一个区间 \([l,r]\) \((l \leq i \leq r)\),则该区间的和 \(sum_{l,r}\) 必定会与 \(a_i\) 相乘恰好一次,并对答案产生相应的贡献。
现在问题转化为:对于每个下标 \(i\),求出 \(b\) 中所有包含该下标的区间的 \(\mathrm{sum}\) 和(记为 \(S_i\))
例如样例一,\(b\) 中所有包含下标 \(2\) 的区间为 \([1,2]\),\([1,3]\),\([2,2]\),\([2,3]\),它们的 \(\mathrm{sum}\) 值依次为 \(3+4=7\),\(3+4+5=12\),\(4=4\),\(4+5=9\). \(S_2=7+12+4+9=32\). 所以 \(a_2\) 对答案产生的贡献为 \(32*3=96\).
尝试用 \(\mathrm{O(n)}\) 的时间复杂度求出每个下标 \(i\) 的 \(S\) 值。设 \(f_i\) 为 \(b\) 中以 \(i\) 为左端点的区间的 \(\mathrm{sum}\) 和,\(g_i\) 为 \(b\) 中以 \(i\) 为右端点的区间的 \(\mathrm{sum}\) 和。得到递推式:
\(f_i=f_{i+1}+b_i \times (n-i+1)\)
\(g_i=g_{i-1}+b_i \times i\)
设位置 \(i\) 的 \(S\) 值为 \(S_i\),有: \(S_i=S_{i-1}+f_i-g_{i-1}\). 其中 \(g_{i-1}\) 表示两端都在 \(i\) 左边的多余区间。
\(ans=\sum_{i=1}^n\) \(S_i \times a_i\). 总复杂度 \(\mathrm{O(n)}\).
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2333333, mod = 1000000007;
long long n, ans = 0, g[N], f[N], S[N], a[N], b[N];
int main()
{
scanf("%lld", &n);
g[0] = S[0] = f[n + 1] = 0;
for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for(int i = 1; i <= n; i++) scanf("%lld", &b[i]);
for(int i = n; i >= 1; i--)
f[i] = (f[i + 1] % mod + (b[i] * (n - i + 1)) % mod) % mod;
for(int i = 1; i <= n; i++)
g[i] = (g[i - 1] % mod + (b[i] * i) % mod) % mod;
for(int i = 1; i <= n; i++)
{
S[i] = (S[i - 1] + f[i] - g[i - 1] + mod) % mod;
ans = (ans + (a[i] * S[i]) % mod) % mod;
}
printf("%lld", ans);
return 0;
}