LuoguP5686 [CSP-S2019 江西] 和积和 题解

题目

P5686 [CSP-S2019 江西] 和积和

题目背景

JXCSP-S T2

题目描述

给定两个下标从 \(1\)\(n\) 编号的序列 \(a_i,b_i\),定义函数 \(S(l,r)(1\le l\le r\le n)\) 为:

\[\sum_{i=l}^r a_i\times \sum_{i=l}^r b_i \]

请你求出下列式子的值:

\[\sum_{l=1}^n \sum_{r=l}^n S(l,r) \]

由于答案可能很大,你只需要给出答案模 \(10^9+7\) 后的结果。

输入格式

第一行一个正整数 \(n\) 表示序列长度。
第二行 \(n\) 个正整数表示 \(a_i\)
第三行 \(n\) 个正整数表示 \(b_i\)

输出格式

仅一行一个整数表示答案模 \(10^9+7\) 后的结果。

输入输出样例 #1

输入 #1

3
2 3 4
3 4 5

输出 #1

244

输入输出样例 #2

输入 #2

5
11 22 33 44 55
12 34 56 78 90

输出 #2

201542

说明/提示

【数据范围】
对于 \(20\%\) 的数据:\(n\le 10\) , \(a_i,b_i\le 10\)
对于 \(40\%\) 的数据:\(n\le 200\) , \(a_i,b_i\le 100\)
对于 \(70\%\) 的数据:\(n\le 3000\) , \(a_i,b_i\le 10^5\)
对于 \(100\%\) 的数据:\(3\le n\le 5\times 10^5\) , \(1\le a_i,b_i\le 10^9\)

分析&做题历程

一:

第一思路自然是直接暴力,通过循环\(l\),\(r\),每次计算\(S(l,r)\)即可

//此为解释原理代码,故没有%mod操作,但实际得写
ll ans = 0;
for(int i=1;i<=n;i++){
	for(int j=i;j<=n;j++){
	//计算S(l,r), 这里为S(i,j) 
		ll  cnt1 = 0,cnt2 = 0;
		for(int k=i;k<=j;k++){
			cnt1 += a[k];
			cnt2 += b[k];
		}
		ans += cnt1*cnt2; 
	}
} 
cout<<ans;

\(提交后20pts\)

二:

第二则是用前缀和优化求\(S(l,r)\)的过程

前缀和知识(会的可跳过)

\(suma_i\)\(a_i\)的前缀和数组,\(sumb_i\)\(b_i\)的前缀和数组
注:

\[suma_i = \sum_{k=1}^i a_k \]

\[sumb_i = \sum_{k=1}^i b_k \]

\[suma_i = a_i \ + suma_{i-1} \]

\[sumb_i = b_i \ + sumb_{i-1} \]

\[\sum_{i=l}^r a_i = \sum_{i=1}^r a_i - \sum_{i=1}^{l-1} a_i \]

\[\sum_{i=l}^r a_i = suma_r - suma_{l-1} \]

\[同理\sum_{i=l}^r b_i = sumb_r - sumb_{l-1} \]

开始

则:\(S(l,r)(1\le l\le r\le n)\)

\[S(l,r) = \sum_{i=l}^r a_i\times \sum_{i=l}^r b_i \]

\[S(l,r) = (suma_r-suma_{l-1}) \times\ (sumb_r-sumb_{l-1}) \]

所以有代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;//答案可能会超int,故开long long保险
const ll mod = 1e9+7;//模数mod
const int N = 5e5+10;//数组大小
int n;
ll tmp;
ll a[N],b[N];
ll ans;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>tmp;
		a[i] = (tmp + a[i-1])%mod;//由于原数组并不会用到,直接用a数组表示原数组的前缀和数组
	}
	for(int i=1;i<=n;i++){
		cin>>tmp;
		b[i] = (tmp + b[i-1])%mod;//由于原数组并不会用到,直接用a数组表示原数组的前缀和数组
	}
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j++){
			ans += (a[j]-a[i-1])*(b[j]-b[i-1])%mod;//优化操作
			ans %= mod;
		}
	}
	cout<<ans;
	return 0;
}

\(提交后70pts,剩余都TLE\)

三:

\(由于是TLE,故还得优化,观察ans的式子\)

\[ans = \sum_{l=1}^n \sum_{r=l}^n S(l,r) \]

\[ans = \sum_{l=1}^n \sum_{r=l}^n \ [(suma_r-suma_{l-1}) \times\ (sumb_r-sumb_{l-1})] \]

\(尝试展开式子优化\)

\[\sum_{l=1}^n \sum_{r=l}^n \ [(suma_r-suma_{l-1}) \times\ (sumb_r-sumb_{l-1})] \]

\[= \sum_{l=1}^n \sum_{r=l}^n \ (suma_r \times sumb_r\ + \ suma_{l-1} \times sumb_{l-1}\ - suma_r\ \times sumb_{l-1} \ - suma_{l-1}\ \times sumb_{r}) \]

\[= \sum_{l=1}^n \sum_{r=l}^n(suma_r \times sumb_r) \ + \sum_{l=1}^n \sum_{r=l}^n(suma_{l-1} \times sumb_{l-1}) \ - \sum_{l=1}^n \sum_{r=l}^n(suma_r\ \times sumb_{l-1})\ - \sum_{l=1}^n \sum_{r=l}^n(suma_{l-1}\ \times sumb_{r}) \]

\(由于原数组没有用到,故用a_i直接表示suma_i,b_i直接表示sumb_i\)

\(故原式为\)

\[= \sum_{l=1}^n \sum_{r=l}^n(a_r \times b_r) \ + \sum_{l=1}^n \sum_{r=l}^n(a_{l-1} \times b_{l-1}) \ - \sum_{l=1}^n \sum_{r=l}^n(a_r\ \times b_{l-1})\ - \sum_{l=1}^n \sum_{r=l}^n(a_{l-1}\ \times b_{r}) \]

\(分开优化:\)
\(1.\)

\[\sum_{l=1}^n \sum_{r=l}^n(a_r \times b_r) \ \]

\[= \sum_{l=1}^n \ [\sum_{r=l}^n(a_r \times b_r)] \ \]

\[定义t_i = a_i \times b_i, 故原式为\sum_{l=1}^n \ (\sum_{r=l}^nt_r) \]

\[再定义T_i为t_i的前缀和数组 \]

\[= \sum_{l=1}^n \ (T_n - T_{l-1}) \]

\(2.\)

\[\sum_{l=1}^n \sum_{r=l}^n(a_{l-1} \times b_{l-1}) \ \]

\[= \sum_{l=1}^n \ [\sum_{r=l}^n(a_{l-1} \times b_{l-1})] \ \]

\[= \sum_{l=1}^n \ (n-i+1) \times (a_{l-1} \times b_{l-1}) \ \]

\(3.\)

\[\sum_{l=1}^n \sum_{r=l}^n(a_r\ \times b_{l-1})\ (注意带负号) \]

\[= \sum_{l=1}^n \sum_{r=l}^n(a_r\ \times b_{l-1})\ \]

\[= \sum_{l=1}^n (b_{l-1}\sum_{r=l}^na_r)\ \]

\[定义A_i为a_i的前缀和数组 \]

\[= \sum_{l=1}^n [b_{l-1} \times (A_n \ - A_{l-1})]\ \]

\(4.\)

\[\sum_{l=1}^n \sum_{r=l}^n(b_r\ \times a_{l-1})\ (注意带负号) \]

\[= \sum_{l=1}^n \sum_{r=l}^n(b_r\ \times a_{l-1})\ \]

\[= \sum_{l=1}^n (a_{l-1}\sum_{r=l}^n b_r)\ \]

\[定义B_i为b_i的前缀和数组 \]

\[= \sum_{l=1}^n [a_{l-1} \times (B_n \ - B_{l-1})]\ \]

\(故原式优化为\)

\[\sum_{l=1}^n\ [(T_n - T_{l-1}) \ + (n-i+1) \times (a_{l-1} \times b_{l-1}) \ - b_{l-1} \times (A_n \ - A_{l-1}) \ - a_{l-1} \times (B_n \ - B_{l-1})] \]

\(其中T_i,t_i,a_i,b_i,A_i,B_i都可以O(n)预处理\)
\(附代码:\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int N = 5e5+10;
int n;
ll tmp;
ll a[N],b[N],A[N],B[N],t[N],T[N];
ll ans;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>tmp;
		a[i] = (tmp + a[i-1])%mod;
		A[i] = (a[i] + A[i-1])%mod;
	}
	for(int i=1;i<=n;i++){
		cin>>tmp;
		b[i] = (tmp + b[i-1])%mod;
		B[i] = (b[i] + B[i-1])%mod;
	}
	
	for(int i=1;i<=n;i++){
		t[i] = (a[i]%mod*b[i]%mod)%mod;
		T[i] = (t[i] + T[i-1])%mod;
	}
	for(int i=1;i<=n;i++){
		ans += (T[n]-T[i-1])%mod;
		ans %= mod;
		ans += (n-i+1)*t[i-1]%mod;
		ans %= mod;
		ans -= (b[i-1]*(A[n]-A[i-1]))%mod;
		ans = (ans + mod ) % mod;
		ans -= (a[i-1]*(B[n]-B[i-1]))%mod;
		ans = (ans + mod ) % mod;
	}
	cout<<ans;
	return 0;
}

\(AC\)

posted @ 2025-10-11 17:09  海南方  阅读(6)  评论(0)    收藏  举报