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 imes (n-i+1))

(g_i=g_{i-1}+b_i imes 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 imes 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;
} 
原文地址:https://www.cnblogs.com/Andy-park/p/14074430.html