[CSP-SJX2019]和积和 题解(思维题)

一道思维题。我还是太菜了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;
}
原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13788042.html