2023NOIP A层联测41 meirin

文章讨论了如何在对两个长度为n的序列进行q次操作后,快速查询每个区间内a值和与b值和乘积之和的总和,利用前缀和和后缀和优化求解,时间复杂度为O(n+q)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给定两个长度为nnn的序列a,ba,ba,b

qqq次操作,每次操作l r k表示将序列bbb中区间[l,r][l,r][l,r]上的每个值加上kkk

每次操作结束后,查询:

∑l=1n∑r=ln(∑i=lrai)×(∑i=lrbi)\sum\limits_{l=1}^n\sum\limits_{r=l}^n(\sum\limits_{i=l}^ra_i)\times (\sum\limits_{i=l}^rb_i)l=1nr=ln(i=lrai)×(i=lrbi)

输出答案模109+710^9+7109+7后的值。

1≤n≤5×105,q≤1061\le n\leq 5\times 10^5,q\leq 10^61n5×105,q106

1≤l≤r≤n,−109≤ai,bi,k≤1091\leq l\leq r\leq n,-10^9\leq a_i,b_i,k\leq 10^91lrn,109ai,bi,k109


题解

我们先考虑转化题目中的式子。可以发现这个式子就是[1,n][1,n][1,n]的每个子区间的aaa值和与bbb值和的乘积之和,那我们枚举每个bbb,看有多少个区间包括bbb,然后求区间的aaa值和。于是

ans=∑i=1nbi×∑l=1i∑r=in∑j=lraians=\sum\limits_{i=1}^nb_i\times \sum\limits_{l=1}^i\sum\limits_{r=i}^n\sum\limits_{j=l}^ra_ians=i=1nbi×l=1ir=inj=lrai

sis_isiaia_iai的前缀和,那么

ans=∑i=1nbi×∑l=1i∑r=insr−sl−1ans=\sum\limits_{i=1}^nb_i\times \sum\limits_{l=1}^i\sum\limits_{r=i}^ns_r-s_{l-1}ans=i=1nbi×l=1ir=insrsl1

后面的∑l=1i∑r=insr−sl−1\sum\limits_{l=1}^i\sum\limits_{r=i}^ns_r-s_{l-1}l=1ir=insrsl1可以变为(i×∑r=insr)−((n−i+1)×∑l=1isl−1)(i\times \sum\limits_{r=i}^ns_r)-((n-i+1)\times \sum\limits_{l=1}^is_{l-1})(i×r=insr)((ni+1)×l=1isl1),这两个部分可以分别用后缀和以及前缀和求出,记vi=∑l=1i∑r=insr−sl−1v_i=\sum\limits_{l=1}^i\sum\limits_{r=i}^ns_r-s_{l-1}vi=l=1ir=insrsl1,那么一开始时答案为∑i=1nbi×vi\sum\limits_{i=1}^nb_i\times v_ii=1nbi×vi

每次将一个区间加上kkk之后,答案会增加∑i=lrk×vi=k×∑i=lrvi\sum\limits_{i=l}^rk\times v_i=k\times \sum\limits_{i=l}^rv_ii=lrk×vi=k×i=lrvi,我们求vvv的前缀和svsvsv,那么每次修改对答案的贡献就是k×(svr−svl−1)k\times (sv_r-sv_{l-1})k×(svrsvl1),这样我们就可以O(1)O(1)O(1)处理每次询问。因为题目只需要求答案,所以我们不需要真的去修改bib_ibi

时间复杂度为O(n+q)O(n+q)O(n+q)

code

#include<bits/stdc++.h>
using namespace std;
const int N=500000;
const long long mod=1e9+7;
int n,q,a[N+5],b[N+5],s[N+5],s1[N+5],s2[N+5];
long long ans=0,v[N+5],sv[N+5];
int main()
{
	freopen("meirin.in","r",stdin);
	freopen("meirin.out","w",stdout);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		a[i]=(a[i]+mod)%mod;
		s[i]=(s[i-1]+a[i])%mod;
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&b[i]);
		b[i]=(b[i]+mod)%mod;
	}
	for(int i=n;i>=1;i--){
		s1[i]=(s1[i+1]+s[i])%mod;
	}
	for(int i=1;i<=n;i++){
		s2[i]=(s2[i-1]+s[i])%mod;
	}
	for(int i=1;i<=n;i++){
		v[i]=1ll*i*s1[i]-1ll*(n-i+1)*s2[i-1];
		v[i]=(v[i]%mod+mod)%mod;
		sv[i]=(sv[i-1]+v[i])%mod;
	}
	for(int i=1;i<=n;i++) ans=(ans+b[i]*v[i])%mod;
	while(q--){
		int l,r,k;
		scanf("%d%d%d",&l,&r,&k);
		k=(k+mod)%mod;
		ans=(ans+(sv[r]-sv[l-1]+mod)%mod*k%mod)%mod;
		printf("%lld\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值