前缀和

首先说明一下题目中的要求值的式子的意思:对于所有的区间[l,r](其中1<=l<=r<=n),求每个区间点对应的数的和(即a[i]的和),将其乘以区间的长度对应的权值(即w[r-l+1]),取和并输出。

理解了上述之后,毫无疑问此题需要用前缀和,解决此题有两种处理途径:枚举点和枚举区间长度。
枚举点:通过计算每个点在不同长度的区间中的贡献,进而求得该点在所有长度的区间中的贡献,可求得所有点在其所有出现过的区间中的贡献。
枚举区间长度:通过计算在给定长度下的一个区间中其中所有点的贡献,进而求得在给定长度下所有区间中所有点的贡献,可求得在所有长度的区间中所有点的贡献。
由于两种方法处理手段基本一致,这里说明第一种方法。

假设点的个数为5,按照上述途径处理:
第1个点在每个长度(1-5)的区间中出现的次数序列为:1,1,1,1,1,其贡献为a[1]* (w[1]+w[2]+w[3]+w[4]+w[5])。
第2个点在每个长度的区间中出现的次数的序列为:1,2,2,2,1,其贡献为a[2]*(w[1]+2w[2]+2w[3]+2w[4]+w[5])。
后三个点生成的序列分别为:1,2,3,2,1;1,2,2,2,1;1,1,1,1,1,贡献可依次求得。

如果按此直接计算必然会超时,这里提供两种优化方法:
1.对于每个序列,可以发现都满足先递增、再保持、后递减这样的模式,且对于递增和递减,权值下标和其系数变化一致(w[1]系数为1,w[2]系数为2...),所以可以采用乘积取前缀和的方法计算前后两个部分,中间一部分因其系数相等可直接利用前缀和计算w的和。
2.观察可以发现,在前几个序列中,每次都是取一段连续子序列加一,也就是加上连续一段的权值,这可以采用前缀和实现;对于后面的序列,每次取一段减一,采用同样的方法处理。

代码:

#include <iostream>
#include <queue>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cctype>
#include <functional>
#include <string>
#include <cstring>
#include <sstream>
#include <deque>
#define fir first
#define sec second
using namespace std;

typedef long long ll;
typedef pair<int,int> P;
typedef pair<P,int> Q;

const int inf1=2e9+9;
const ll inf2=8e18+9;
const ll mol=1e9+7;
const int maxn=3e5+9;
const ll maxx=1e12+9;

int n;
ll ar[maxn],w[maxn];
ll sum[maxn];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&ar[i]);
    for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
    for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+w[i])%mol;
    ll ans=0,tmp=0;
    for(int i=1;i<=n;i++)
    {
        tmp=(tmp+(sum[n-i+1]-sum[i-1]+mol)%mol)%mol;
        ans=(ans+ar[i]*tmp)%mol;
    }
    printf("%lld\n",ans);
}