Luogu·和积和【数论】【前缀和】

Description–

给定两个下标从 1 1 1 n n n编号的序列 a i , b i ai, bi ai,bi
定义函数 S ( l , r ) ( 1 < = l < = r < = n ) S(l, r)(1 <= l <= r <= n) S(l,r)(1<=l<=r<=n)在这里插入图片描述

请你求出下列式子的值:在这里插入图片描述
由于答案可能很大,你只需要给出答案模 1 0 9 + 7 10^9 + 7 109+7 后的结果。​


Input–

第一行一个正整数 n n n表示序列长度
第二行 n n n个正整数表示 a i ai ai
第三行 n n n个正整数表示 b i bi bi

Output–

仅一行一个整数表示答案模 1 0 9 + 7 10^9 + 7 109+7 后的结果。


Sample Input–

样例1

3
2 3 4
3 4 5

样例2

244

Sample Output–

样例1

5
11 22 33 44 55
12 34 56 78 90

样例2

201542

说明–

对于 10% 的数据 n < = 10 , a i , b i < = 10 n <= 10, ai,bi <= 10 n<=10ai,bi<=10
对于 10% 的数据 n < = 200 , a i , b i < = 200 n <= 200, ai,bi <= 200 n<=200ai,bi<=200
对于 10% 的数据 n < = 3000 , a i , b i < = 1 0 5 n <= 3000, ai,bi <= 10^5 n<=3000ai,bi<=105
对于 10% 的数据 3 < = n < = 5 ∗ 1 0 5 , 1 < = a i , b i < = 5 ∗ 1 0 5 3 <= n <= 5 * 10^5, 1 <= ai,bi <= 5 * 10^5 3<=n<=51051<=ai,bi<=5105


解题思路–

显然,暴力不太可

设前缀和 a a a p a pa pa,前缀和 b b b p b pb pb

70pts

先前缀和处理数组 a a a和数组 b b b
S ( l , r ) S(l, r) S(l,r)可以优化为 ( p a [ r ] − p a [ l − 1 ] ) ∗ ( p b [ r ] − p b [ r − 1 ] ) (pa[r] - pa[l - 1]) * (pb[r] - pb[r - 1]) (pa[r]pa[l1])(pb[r]pb[r1])

100pts

对式子进行拆分优化

n = 2 n = 2 n=2 为例

S(1, 1) = pa[1] * pb[1]
S(1, 2) = pa[2] * pb[2]
S(2, 2) = pa[2] * pb[2] - pa[1] * pb[2] - pa[2] * pb[1] + pa[1] * pb[1]

则结果为S(1, 1) + S(1, 2) + S(2, 2)

= pa[1] * pb[1] + pa[2] * pb[2] + pa[2] * pb[2] - pa[1] * pb[2] - pa[2] * pb[1] + pa[1] * pb[1]

= 2 * (pa[1] * pb[1] + pa[2] * pb[2]) - (pa[1] * pb[2] + pa[2] * pb[1])

然后减去 (pa[1] * pb[1] + pa[2] * pb[2]) 再加上 (pa[1] * pb[1] + pa[2] * pb[2])

3 * (pa[1] * pb[1] + pa[2] * pb[2]) - (pa[1] + pa[2]) * (pb[1] + pb[2])

于是式子的规律为 在这里插入图片描述


代码–

70pts
#include <iostream>
#include <cstdio>
#define ll long long

using namespace std;

const ll M = 1e9 + 7;

ll n, ans;

struct ooo
{
	int x, sum;
}a[500005], b[500005];

ll s(ll x, ll y)
{
	ll aa = a[y].sum - a[x - 1].sum;
	ll bb = b[y].sum - b[x - 1].sum;
    return aa * bb % M;
}

int main()
{
	scanf("%lld", &n);
	for (ll i = 1; i <= n; ++i)
	  scanf("%lld", &a[i].x), a[i].sum = (a[i - 1].sum + a[i].x) % M;//前缀和
	for (ll i = 1; i <= n; ++i)
	  scanf("%lld", &b[i].x), b[i].sum = (b[i - 1].sum + b[i].x) % M;
	for (ll l = 1; l <= n; ++l)
	  for (ll r = l; r <= n; ++r)
	    ans = (ans + s(l, r)) % M;
	printf("%lld", ans);
	
	return 0;
}
100pts
#include <iostream>
#include <cstdio>
#define ll long long

using namespace std;

const ll M = 1e9 + 7;

ll n, ans, suma, sumb;

struct ooo
{
	int x, ss;
}a[500005], b[500005];

int main()
{
	scanf("%lld", &n);
	for (ll i = 1; i <= n; ++i)
	{
		scanf("%lld", &a[i].x);
		a[i].ss = (a[i - 1].ss + a[i].x) % M;
		suma = (suma + a[i].ss) % M;
	}
	for (ll i = 1; i <= n; ++i)
	{
		scanf("%lld", &b[i].x);
		b[i].ss = (b[i - 1].ss + b[i].x) % M;
		sumb = (sumb + b[i].ss) % M;
	}
	for (int i = 1; i <= n; ++i)
	  ans = (ans + (((n + 1) * a[i].ss) % M * b[i].ss) % M) % M;
	ans = (ans - suma * sumb % M + M) % M;
	printf("%lld", ans);
	
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值