链接:
@[toc]
时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 131072K,其他语言262144K 64bit IO Format: %lld
题目描述
一个数轴,每一个储物点会有一些东西,同时它们之间存在距离。
每次给个区间[l,r],查询把这个区间内所有储物点的东西运到另外一个储物点的代价是多少? 比如储物点i有x个东西,要运到储物点j,代价为x* dist( i , j ) dist就是储物点间的距离。
输入描述:
第一行两个数表示n,m
第二行n-1个数,第i个数表示第i个储物点与第i+1个储物点的距离ai
第三行n个数,表示每个储物点的东西个数bi
之后m行每行三个数x l r
表示查询要把区间[l,r]储物点的物品全部运到储物点x的花费 每次查询独立
输出描述:
对于每个询问输出一个数表示答案 答案对1000000007取模
示例1
输入
5 5 2 3 4 5 1 2 3 4 5 1 1 5 3 1 5 2 3 3 3 3 3 1 5 5
输出
125 72 9 0 70
备注:
对于100%的数据n,m <= 200000 , 0 <= ai,bi <= 2000000000
题解:
先要意识到给的距离是两个相邻的储物点的距离,我们可以据此先维护一个距离的前缀和,这样我们就可以求任意两点之间的距离。
同理,每个储物点都是存有物品的,也用前缀和来维护,这就可以求出某个区间内物品的总数量。
我们还需要用一个数组c来记录,给定区间[l,r]内所有商品距离1号仓库的总和,c[i]=c[i-1]+a[i]*b[i],
a,b,c分别代表上面三个的前缀和
有什么用呢?接着往下看
如果所给x在[l,r]的右边,也就是x>=r,当x从i变为i+1,相当于整个区间内的物品都多移动了一段距离,长度为len(i,i+1),那我们就可从i点答案的基础上直接改,从答案基础上加上[l,r]的物品数量 * len(i,i+1),也就是 ( b [ r ] - b [ l - 1 ] ) * ( a [ x ] ) - ( c [ r ] - c[ l - 1 ] )
怎么理解:[l,r]这个区间内所有物品全部从起始点移动到x点,[1,l-1]这一块是多移动了,比如看l点,从1 ~ l 再从l ~ x ,其中1~l是多移动的部分,就要剪除,l点物品总量*所有物品移动到点1的距离,这不正是我们求得c,所以减去( c [ r ] - c[ l - 1 ] )
如果x在[l,r]的左侧,也就是x<=r,当x从i变为i+1,就是和上面反过来,少移动了一段距离,距离为len(i,i+1),答案就是( c [ r ] - c[ l - 1 ] ) - ( b [ r ] - b [ l - 1 ] ) * ( a [ x ] )
如果x在[l,r]中间,你就把[l,r]分成两个区域,然后[l.x]与[x+1,r]用上面的方法对待
对了,别忘了取模
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+5; const ll mod=1e9+7; ll a[maxn],b[maxn],c[maxn]; ll getr(ll x,ll l,ll r)//当x在右边 { ll ans=(((b[r]-b[l-1])*a[x]%mod-((c[r]-c[l-1])%mod+mod)%mod)%mod+mod)%mod; return ans; } ll getl(ll x,ll l,ll r)//x在左边 { ll ans=(((c[r]-c[l-1])%mod+mod)%mod-((b[r]-b[l-1])*a[x]%mod)%mod+mod)%mod; return ans; } ll x,l,r; ll sum; int main() { int n,m; cin>>n>>m; for(int i=2;i<=n;i++) { cin>>a[i]; a[i]=(a[i-1]+a[i])%mod; } for(int i=1;i<=n;i++) { cin>>b[i]; c[i]=(c[i-1]+a[i]*b[i]%mod)%mod; b[i]=(b[i]+b[i-1])%mod; } while(m--) { sum=0; cin>>x>>l>>r; if(r<=x) { sum=getr(x,l,r); } else if(l>=x) { sum=getl(x,l,r); } else { sum+=getr(x,l,x)+getl(x,x+1,r); } printf("%lld\n",sum%mod); } return 0; }