洛谷 P5686 [CSP-SJX2019]和積和

傳送門

思路

應用多個前綴和推出式子即可

30pts30pts
首先如果暴力算的話很簡單,直接套三層循環就好了(真的是三層!!最後兩個sigmasigma一起算就好了)
l=1nr=lni=lra[i]i=lrb[i]\sum_{l = 1}^{n}\sum_{r = l}^{n}\sum_{i = l}^{r}a[i]\sum_{i = l}^{r}b[i]

70pts70pts
其實不用這麼麻煩,我們發現最後兩個sigmasigma可以用前綴和O(1)O(1)算出來,這樣就可以7070分了(見代碼sub1sub1

100pts100pts
考慮再拆一層循環(這裏用SASASBSB數組表示70分鐘aa數組和bb數組的前綴和)

我覺得你可以自己展開式子畢竟不能太懶嘛

然後就會得到這樣一個式子

l=1n(SA[l]SB[l]+SA[l+1]SB[l+1]+...+SA[n]SB[n])(SA[l]+SA[l+1]+...+SA[n])SB[l1]\sum_{l = 1}^{n}(SA[l] * SB[l] + SA[l + 1] * SB[l + 1] + ...+SA[n] * SB[n]) - (SA[l] + SA[l + 1] + ... + SA[n]) * SB[l - 1]
(SB[l]+SB[l+1]+...+SB[n])SA[l1]+(nl+1)SA[l1]SB[l1]- (SB[l] + SB[l + 1] + ... + SB[n]) * SA[l - 1] + (n - l + 1) * SA[l - 1] * SB[l - 1]

我們發現這些東西基本都可以用前綴和再求一遍,所以我們要求一下(SA[l]SB[l]+SA[l+1]SB[l+1]+...+SA[n]SB[n])(SA[l] * SB[l] + SA[l + 1] * SB[l + 1] + ...+SA[n] * SB[n])的前綴和(代碼中爲wocwoc數組),求一下SASA數組的前綴和(代碼中爲qzaqza數組),求一下SBSB數組的前綴和(代碼中爲qzbqzb數組),然後就可以O(n)O(n)做這道題啦

下面上代碼

代碼

#include <cstdio>
#include <cstring>
#include <iostream>
#define int long long
using namespace std;

const int A = 5e5 + 11;
const int mod = 1e9 + 7;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; 
	for( ; isdigit(c); c = getchar()) x = x * 10 + c - 48;
	return x * f;
}

int n, a[A], b[A], ans = 0, SA[A], SB[A], qza[A], qzb[A];
int woc[A];

void sub1() {
	for(int l = 1; l <= n; l++) {
		for(int r = l; r <= n; r++) {
			ans = (ans + ((SA[r] - SA[l - 1]) % mod + mod) % mod * ((SB[r] - SB[l - 1]) % mod + mod)) % mod;
		}
	}
	cout << ans << '\n';
	return;
}

signed main() {
	n = read();
	for(int i = 1; i <= n; i++) a[i] = read(), SA[i] = (SA[i - 1] + a[i]) % mod;
	for(int i = 1; i <= n; i++) b[i] = read(), SB[i] = (SB[i - 1] + b[i]) % mod;
	if(n <= 3000) return sub1(), 0;
	for(int i = 1; i <= n; i++) woc[i] = (woc[i - 1] + SA[i] * SB[i]) % mod; 
	for(int i = 1; i <= n; i++) qza[i] = (qza[i - 1] + SA[i]) % mod; 
	for(int i = 1; i <= n; i++) qzb[i] = (qzb[i - 1] + SB[i]) % mod;
	for(int i = 1; i <= n; i++) {
		int fuck1 = ((woc[n] - woc[i - 1]) % mod + mod) % mod;
		int fuck2 = ((qza[n] - qza[i - 1]) % mod + mod) % mod * SB[i - 1] % mod;	
		int fuck3 = ((qzb[n] - qzb[i - 1]) % mod + mod) % mod * SA[i - 1] % mod;
		int fuck4 = (((n - i + 1) % mod * SA[i - 1] % mod * SB[i - 1]) % mod + mod) % mod;
		int now = fuck1 - fuck2 - fuck3 + fuck4;
		ans = ((ans + now % mod) % mod + mod) % mod;
	}
	cout << ans << '\n';
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章