洛谷10月月赛R2·浴谷八连测R3 -Chtholly-T1

原题链接:https://www.luogu.org/problem/show?pid=3932

月赛的时候只做了这一道题,而且暴力还写炸了。。。

其实,并不需要算很多的前缀和的

a[i]表示i位置的物品数

dis[i]表示每个储物点与一号的距离

sum表示前i个储物点(物品数*距离)的和

在x<l的时候,ans=Σ(dis[i]-dis[x])*a[i]=Σ(a[i]*dis[i]-a[i]*dis[x])=Σ(a[i]*dis[x])-dis[x]*Σ(a[i])

同理,x>r时,ans=dis[x]*Σ(a[i])-Σ(a[i]*dis[x]);

发现这两种公式明显可以用前缀和优化,a[i]和dis[i]在读入过程中算出他们的前缀和,sum[i]=a[i]*dis[i]

于是x<l时的计算式就变成了这样:ans=sum[r]-sum[l-1]+dis[x]*(a[r]-a[l-1])

而x>=l&&x<=r时,将区间分为[l,x-1][x+1,r]两部分分别计算即可

注意减法的取模问题,不论是暴力还是正解我都是挂在这个地方。

本题的减法很多,写个函数来处理减法的取模会方便很多

#include<cstdio>
const long long c=19260817;
void read(long long &y)
{
    y=0;char x=getchar();
    while(x<'0'||x>'9') x=getchar();
    while(x>='0'&&x<='9')
    {
        y=y*10+x-'0';
        x=getchar();
    }
}
long long ch(long long s,long long t)
{
    return (s-t+c)%c;
}
long long sum[200005],dis[200005],a[200005],x,l,r,ans;
int n,m;
int main()
{
//    freopen("68.in","r",stdin);
    scanf("%d %d",&n,&m);
    for(int i=2;i<=n;i++)
    {
        read(dis[i]);
        dis[i]=(dis[i]+dis[i-1])%c;
    }
    for(int i=1;i<=n;i++)
    {
        read(a[i]);
        sum[i]=(sum[i-1]+(a[i]*dis[i])%c)%c;
        a[i]=(a[i]+a[i-1])%c;
    }
    for(int i=1;i<=m;i++)
    {
        ans=0;
        read(x);read(l);read(r);
        if(x<l) ans=ch(ch(sum[r],sum[l-1]),(dis[x]*ch(a[r],a[l-1]))%c);
        if(x>=l&&x<=r)
        {
            ans+=ch(ch(sum[r],sum[x]),(dis[x]*ch(a[r],a[x]))%c);
            ans+=ch((dis[x]*ch(a[x],a[l-1]))%c,ch(sum[x],sum[l-1]));
            ans%=c;
        }
        if(x>r) ans=ch((dis[x]*ch(a[r],a[l-1]))%c,ch(sum[r],sum[l-1]));
        printf("%lld
",ans);
    }
    return 0;
}