[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;
}

 

posted @ 2020-10-09 20:12  我亦如此向往  阅读(213)  评论(0编辑  收藏  举报