2023NOIP A层联测32 meirin

题目大意

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

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

每次操作结束后,查询:

∑ l = 1 n ∑ r = l n ( ∑ i = l r a i ) × ( ∑ i = l r b i ) \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)

输出答案模 1 0 9 + 7 10^9+7 109+7后的值。

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

1 ≤ l ≤ r ≤ n , − 1 0 9 ≤ a i , b i , k ≤ 1 0 9 1\leq l\leq r\leq n,-10^9\leq a_i,b_i,k\leq 10^9 1lrn,109ai,bi,k109


题解

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

a n s = ∑ i = 1 n b i × ∑ l = 1 i ∑ r = i n ∑ j = l r a i ans=\sum\limits_{i=1}^nb_i\times \sum\limits_{l=1}^i\sum\limits_{r=i}^n\sum\limits_{j=l}^ra_i ans=i=1nbi×l=1ir=inj=lrai

s i s_i si a i a_i ai的前缀和,那么

a n s = ∑ i = 1 n b i × ∑ l = 1 i ∑ r = i n s r − s l − 1 ans=\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 = 1 i ∑ r = i n s r − s l − 1 \sum\limits_{l=1}^i\sum\limits_{r=i}^ns_r-s_{l-1} l=1ir=insrsl1可以变为 ( i × ∑ r = i n s r ) − ( ( n − i + 1 ) × ∑ l = 1 i s l − 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),这两个部分可以分别用后缀和以及前缀和求出,记 v i = ∑ l = 1 i ∑ r = i n s r − s l − 1 v_i=\sum\limits_{l=1}^i\sum\limits_{r=i}^ns_r-s_{l-1} vi=l=1ir=insrsl1,那么一开始时答案为 ∑ i = 1 n b i × v i \sum\limits_{i=1}^nb_i\times v_i i=1nbi×vi

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

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

code

#include
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;
}

你可能感兴趣的:(题解,题解,c++)