[CSP-SJX2019]和积和 题解(思维题)
一道思维题。我还是太菜了QAQ,一道黄题做半天……太草了。
题目大意:求$\sum\limits_{i=1}^n\sum\limits_{j=i}^n\sum\limits_{k=i}^j a_k\times \sum\limits_{l=i}^j b_l$
$70pts$是白给的。直接前缀和优化然后$O(n^2)$搞就完事了。
刚开始看到四个$\sum$内心是自闭的。但是转念一想不可能提高组T2考毒瘤数论题,于是开始找规律。对于数数题一个常见的套路就是考虑每个元素的贡献。我们可以发现:对于一对数$(a_i,b_j)$,只有当$l\leq i$且$r\geq j$时$a_i \times b_j$才对答案有贡献。现在我们考虑的就是它对答案贡献的次数。显然贡献次数和$l,r$可以活动的范围有关。$l$的取值范围为$\min(i,j)$,$r$的取值范围为$\min(n-i+1,n-j+1)$。所以有:
$ans=\sum\limits_{i=1}^n \sum\limits_{j=1}^n \min(i,j) \times \min(n-i+1,n-j+1)\times a_ib_j$
接下来考虑的是优化。我们维护两个前缀和,分别表示$\sum\limits_{i=1}^n i\times b_i$和$\sum\limits_{i=1}^n (n-i+1)\times b_i$,以$a_i$为基准计算答案。对于$j$我们分类讨论:当$j\leq i$时答案有$j\times (n-i+1)\times a_ib_j$;当$j>i$时答案有$i\times (n-j+1)\times 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; }