题解 洛谷 P5686 和积和

首先暴力的看待问题,利用前缀和 sa 和 sb 有朴素的n方做法,70pts get!

参考代码片段:

for(int l = 1; l <= n; ++l) {
    for(int r = l; r <= n; ++r) {
        ans = ans + (sa[r]-sa[l-1]) * (sb[r]-sb[l-1]);
    }
}

然后我们把最内层的式子中两括号相乘拆开得到:

\[sa(r)sb(r)-sa(l-1)sb(r)-sa(r)sb(l-1)+sa(l-1)sb(l-1) \]

发现两个+项 只和 \(l\) 或 只和 \(r\) 有关,直接一层循环解决。

对于负项,如果我们将 \(l\) 固定,会发现与 \(r\) 有关的部分是一段前缀和的和

于是就可以求前缀和的前缀和,快乐地A掉这道题辣qwq!

另外注意取模细节,前缀和相减可能减出负数。

参考完整代码:

/*code by Szzkp*/

#include <bits/stdc++.h>

#define Rei register int
#define ll long long

using namespace std;

const int inf = 0x3f3f3f3f;

inline ll read() {
	ll x = 0;
	char f = 0, c = getchar();
	while(!isdigit(c)) f |= (c=='-'), c = getchar();
	if(f) while(isdigit(c)) x = x*10-c+48, c = getchar();
	else while(isdigit(c)) x = x*10+c-48, c = getchar();
	return x;
}

const int N = 5e5+5;
const ll mod = 1e9+7;

ll n;
ll sa[N], sb[N];
ll ssa[N], ssb[N];
ll ans;

int main() {
	n = read();
	for(Rei i = 1; i <= n; ++i) {
		sa[i] = (read() + sa[i-1])%mod;
	}
	for(Rei i = 1; i <= n; ++i) {
		sb[i] = (read() + sb[i-1])%mod;
		ans = (ans + i*sa[i]%mod*sb[i]%mod + (n-i+1)*sa[i-1]%mod*sb[i-1]%mod)%mod;
	}
	for(Rei i = 1; i <= n; ++i) {
		ssa[i] = (ssa[i-1]+sa[i])%mod;
		ssb[i] = (ssb[i-1]+sb[i])%mod;
	}
	for(Rei i = 1; i <= n; ++i) {
		ans = (ans - sa[i-1]*(ssb[n]-ssb[i-1])%mod - sb[i-1]*(ssa[n]-ssa[i-1])%mod + mod) % mod;
	}
	cout << (ans<0? ans+mod:ans) << "\n";
	return 0;
}

撒花!✿✿ヽ(°▽°)ノ✿

上一篇:学习笔记——SA求解旅行商问题的理解与代码分析


下一篇:2021-05-19