洛谷P3932 浮游大陆的68号岛

题目描述

有一天小妖精们又在做游戏。这个游戏是这样的。

妖精仓库的储物点可以看做在一个数轴上。每一个储物点会有一些东西,同时他们之间存在距离。

每次他们会选出一个小妖精,然后剩下的人找到区间[l,r]储物点的所有东西,清点完毕之后问她,把这个区间内所有储物点的东西运到另外一个仓库的代价是多少?

比如储物点i有x个东西,要运到储物点j,代价为

x × d i s t ( i , j ) x \times \mathrm{dist}( i , j ) x×dist(i,j)
dist就是仓库间的距离。

当然啦,由于小妖精们不会算很大的数字,因此您的答案需要对19260817取模。

输入格式:

第一行两个数表示n,m
第二行n-1个数,第ii个数表示第ii个储物点与第i+1个储物点的距离

第三行n个数,表示每个储物点的东西个数

之后m行每行三个数x l r

表示查询要把区间[l,r]储物点的物品全部运到储物点x的花费

输出格式:

对于每个询问输出一个数表示答案

说明

对于30%的数据,n,m≤1000
对于另外20%的数据,所有储物点间的距离都为1
对于另外20%的数据,所有储物点的物品数都为1
对于100%的数据 , n , m ≤ 200000 ; a i , b i &lt; = 2 ⋅ 1 0 9 n , m \le 200000 ; a_i , b_i &lt;= 2\cdot 10^9 n,m200000;ai,bi<=2109


题目分析

参考了luogu题解某dalao
s u m D [ i ] sumD[i] sumD[i]记录从 i i i 1 1 1的距离
s u m A [ i ] sumA[i] sumA[i]是物品数量 a [ i ] a[i] a[i]的前缀和
s u m D A [ i ] sumDA[i] sumDA[i]记录 s u m D [ i ] ∗ a [ i ] sumD[i]*a[i] sumD[i]a[i]的前缀和

对于每次询问区间我们拆成两个小区间 [ l l , x − 1 ] , [ x + 1 , r r ] [ll,x-1],[x+1,rr] [ll,x1],[x+1,rr]
以左边这个为例,其运输总费用为
∑ i = l l x − 1 ( s u m D [ x ] − s u m D [ i ] ) ∗ a [ i ] \sum_{i=ll}^{x-1}(sumD[x]-sumD[i])*a[i] i=llx1(sumD[x]sumD[i])a[i]
∑ i = l l x − 1 s u m D [ x ] ∗ a [ i ] − s u m D [ i ] ∗ a [ i ] \sum_{i=ll}^{x-1}sumD[x]*a[i]-sumD[i]*a[i] i=llx1sumD[x]a[i]sumD[i]a[i]
s u m D [ x ] ∑ i = l l x − 1 a [ i ] − ∑ i = l l x − 1 s u m D [ i ] ∗ a [ i ] sumD[x]\sum_{i=ll}^{x-1}a[i]-\sum_{i=ll}^{x-1}sumD[i]*a[i] sumD[x]i=llx1a[i]i=llx1sumD[i]a[i]
s u m D [ x ] ∗ ( s u m A [ x − 1 ] − s u m A [ l l − 1 ] ) − ( s u m D A [ x − 1 ] − s u m D A [ l l − 1 ] ) sumD[x]*(sumA[x-1]-sumA[ll-1])-(sumDA[x-1]-sumDA[ll-1]) sumD[x](sumA[x1]sumA[ll1])(sumDA[x1]sumDA[ll1])

对于右边的区间用同样的方法推导发现就是对上面这个答案取个反
( s u m D A [ x − 1 ] − s u m D A [ l l − 1 ] ) − s u m D [ x ] ∗ ( s u m A [ x − 1 ] − s u m A [ l l − 1 ] ) (sumDA[x-1]-sumDA[ll-1])-sumD[x]*(sumA[x-1]-sumA[ll-1]) (sumDA[x1]sumDA[ll1])sumD[x](sumA[x1]sumA[ll1])
也就是说记录三个前缀和就可以 O ( 1 ) O(1) O(1)查询了


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
 
lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int mod=19260817;
const int maxn=200010;
int n,m;
lt d[maxn],a[maxn];
lt sumD[maxn],sumA[maxn],sumDA[maxn];

lt qsum(int x,int ll,int rr,int f){
	if(ll>rr) return 0;
	lt res=sumD[x]%mod*(sumA[rr]-sumA[ll-1])%mod-(sumDA[rr]-sumDA[ll-1])%mod;
	return (res*f%mod+mod)%mod;
}

int main()
{
    n=read();m=read();
    for(int i=2;i<=n;++i) d[i]=read()%mod;
    for(int i=1;i<=n;++i)
    {
    	a[i]=read()%mod;
		sumA[i]=(sumA[i-1]+a[i])%mod;
		sumD[i]=(sumD[i-1]+d[i])%mod;
		sumDA[i]=(sumDA[i-1]+a[i]*sumD[i])%mod;
	} 
	
	while(m--)
	{
		int x=read(),ll=read(),rr=read();
		lt ansl=qsum(x,ll,min(rr,x-1),1);
		lt ansr=qsum(x,max(ll,x+1),rr,-1);
		printf("%lld\n",(ansl+ansr)%mod);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值