一道思维题。我还是太菜了QAQ,一道黄题做半天……太草了。
题目大意:求$sumlimits_{i=1}^nsumlimits_{j=i}^nsumlimits_{k=i}^j a_k imes sumlimits_{l=i}^j b_l$
$70pts$是白给的。直接前缀和优化然后$O(n^2)$搞就完事了。
刚开始看到四个$sum$内心是自闭的。但是转念一想不可能提高组T2考毒瘤数论题,于是开始找规律。对于数数题一个常见的套路就是考虑每个元素的贡献。我们可以发现:对于一对数$(a_i,b_j)$,只有当$lleq i$且$rgeq j$时$a_i imes b_j$才对答案有贡献。现在我们考虑的就是它对答案贡献的次数。显然贡献次数和$l,r$可以活动的范围有关。$l$的取值范围为$min(i,j)$,$r$的取值范围为$min(n-i+1,n-j+1)$。所以有:
$ans=sumlimits_{i=1}^n sumlimits_{j=1}^n min(i,j) imes min(n-i+1,n-j+1) imes a_ib_j$
接下来考虑的是优化。我们维护两个前缀和,分别表示$sumlimits_{i=1}^n i imes b_i$和$sumlimits_{i=1}^n (n-i+1) imes b_i$,以$a_i$为基准计算答案。对于$j$我们分类讨论:当$jleq i$时答案有$j imes (n-i+1) imes a_ib_j$;当$j>i$时答案有$i imes (n-j+1) imes a_ib_j$。
这样我们就在$O(n)$的时间内解决了这道问题。
不知道为什么炸$long long$了QAQ,于是写了龟速乘,慢的要死……
代码:
#include<cstdio> #include<iostream> #define int long long using namespace std; const int N=500005; const int p=1e9+7; int n,a[N],b[N],ans; int sum1[N],sum2[N]; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } inline int qcal(int x,int y) { int res=0; while(y) { if (y&1) res=(res+x)%p; x=(x+x)%p; y>>=1; } return res%p; } signed main() { n=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=n;i++) { b[i]=read(); sum1[i]=(sum1[i-1]+qcal(i,b[i]))%p; sum2[i]=(sum2[i-1]+qcal(n-i+1,b[i]))%p; } for (int i=1;i<=n;i++) { (ans+=qcal(qcal(n-i+1,a[i]),sum1[i]))%=p; (ans+=qcal(sum2[n]-sum2[i],qcal(i,a[i])))%=p; } printf("%lld",ans); return 0; }