这是一个前缀和+分类讨论题,难点在前缀和应该要做成什么样子和算不同情况应该怎么计算。
前缀和有3个为:距离前缀和dis,数量前缀和num,1~i号仓库的物品都运送到1号点的前缀和sum。
然后分成3种情况讨论:
x在区间右边,x在区间左边,x在区间中间。
计算方法只有两种:把区间内的东西运送到左边和运送到右边。
对于第一种计算方法级记作calR:
先算出区间内的数量num[r]-num[l-1],然后我们知道前缀和sum是1~i号点都送到1号点的和,那么sum[r]-sum[l-1]就是这段区间送到1号点的和,然后减去1到x这段距离dis[x]就行了
即: (这里就不取模了)
对于第二种计算放假记作calL:
同时也先算出区间数量,然后我们把区间数量×dis[x],这样会多出sum[r]-sum[l-1]这段距离,减掉即可。
即:
然后对于三种情况:
第一种:直接calL(l,r,x)
第二种:直接calR(l,r,x)
第三种:calL(x,z,z)+calR(z,y,z)
代码:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#include<queue>
#define fs first
#define se second
#define pb push_back
#define cppio ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> VI;
const int maxn=1e6+6;
const ll inf=0x3f3f3f3f;
const ll mod=1e9+7;
int n,m;
ll num[maxn];
ll dis[maxn];
ll sum[maxn];
ll calL(int l,int r,int x){
ll ans=0;
ll tnum=(num[r]-num[l-1]+mod)%mod;
ans=tnum*dis[x]%mod;
ll tmp=(sum[r]-sum[l-1]+mod)%mod;
ans=(ans+mod-tmp)%mod;
return ans;
}
ll calR(int l,int r,int x){
ll ans=0;
ll tnum=(num[r]-num[l-1]+mod)%mod;
ans=(sum[r]+mod-sum[l-1])%mod;
ans=(ans-tnum*dis[x]%mod+mod)%mod;
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=2;i<=n;i++){
scanf("%lld",dis+i);
dis[i]=(dis[i-1]+dis[i])%mod;
}
for(int i=1;i<=n;i++){
scanf("%lld",num+i);
sum[i]=(sum[i-1]+num[i]*dis[i])%mod;
num[i]=(num[i-1]+num[i])%mod;
}
for(int i=1;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&z,&x,&y);
ll ans=0;
if(z>=y){
ans=calL(x,y,z);
}
else if(z<=x){
ans=calR(x,y,z);
}
else{
ans=(calL(x,z,z)+calR(z,y,z))%mod;
}
printf("%lld\n",ans%mod);
}
return 0;
}


京公网安备 11010502036488号