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;
} 
posted @ 2019-12-11 21:17  Nyxia  阅读(55)  评论(0编辑  收藏  举报