[ACM]【prefix/求和/取余】牛客练习赛64 序列卷积之和

序列卷积之和

传送门
题意:求 ∑ l = 1 n ∑ r = l n ∑ i = l r ∑ j = i r a i × a j m o d    1 e 9 + 7 \sum_{l=1}^n\sum_{r=l}^n\sum_{i=l}^r\sum_{j=i}^ra_i\times a_j\mod 1e9+7 l=1nr=lni=lrj=irai×ajmod1e9+7
题目

思路:

本弱鸡只会找规律(。看到题解恍然大悟。
就是强行用前缀和把循环一层一层剥掉。
一开始是

for(int l=1;l<=n;l++)
	for(int r=l;r<=n;r++)
		for(int i=l;i<=r;i++)
			for(int j=i;j<=r;j++)
				sum+=a[i]*a[j];

把最内层循环展开,是
a [ i ] ∗ a [ i ] + a [ i ] ∗ a [ i + 1 ] + . . . + a [ i ] ∗ a [ r ] a[i]*a[i]+a[i]*a[i+1]+...+a[i]*a[r] a[i]a[i]+a[i]a[i+1]+...+a[i]a[r]

a [ i ] ∗ ( a [ i ] + a [ i + 1 ] + . . . + a [ r ] ) a[i]*(a[i]+a[i+1]+...+a[r]) a[i](a[i]+a[i+1]+...+a[r])
我们求 a [ i ] a[i] a[i]的前缀和,设为 b [ i ] b[i] b[i]

a [ i ] ∗ ( b [ r ] − b [ i − 1 ] ) a[i]*(b[r]-b[i-1]) a[i](b[r]b[i1])

for(int l=1;l<=n;l++)
	for(int r=l;r<=n;r++)
		for(int i=l;i<=r;i++)
			sum+=a[i]*(b[r]-b[i-1]);

把最内层循环展开,是
a [ l ] ∗ ( b [ r ] − b [ l − 1 ] ) + a [ l + 1 ] ∗ ( b [ r ] − b [ l + 1 − 1 ] ) + . . . + a [ r ] ∗ ( b [ r ] − b [ r − 1 ] ) a[l]*(b[r]-b[l-1])+a[l+1]*(b[r]-b[l+1-1])+...+a[r]*(b[r]-b[r-1]) a[l](b[r]b[l1])+a[l+1](b[r]b[l+11])+...+a[r](b[r]b[r1])
拆开小括号,重新组合,得
b [ r ] ∗ ( a [ l ] + a [ l + 1 ] + . . . + a [ r ] ) − ( a [ l ] ∗ b [ l − 1 ] + a [ l + 1 ] ∗ b [ l + 1 − 1 ] + . . . + a [ r ] ∗ a [ r − 1 ] ) b[r]*(a[l]+a[l+1]+...+a[r])-(a[l]*b[l-1]+a[l+1]*b[l+1-1]+...+a[r]*a[r-1]) b[r](a[l]+a[l+1]+...+a[r])(a[l]b[l1]+a[l+1]b[l+11]+...+a[r]a[r1])

b [ r ] ∗ ( b [ r ] − b [ l − 1 ] ) − ( a [ l ] ∗ b [ l − 1 ] + a [ l + 1 ] ∗ b [ l + 1 − 1 ] + . . . + a [ r ] ∗ a [ r − 1 ] ) b[r]*(b[r]-b[l-1])-(a[l]*b[l-1]+a[l+1]*b[l+1-1]+...+a[r]*a[r-1]) b[r](b[r]b[l1])(a[l]b[l1]+a[l+1]b[l+11]+...+a[r]a[r1])
我们求 a [ i ] ∗ b [ i − 1 ] a[i]*b[i-1] a[i]b[i1]的前缀和,设为 c [ i ] c[i] c[i]

b [ r ] ∗ ( b [ r ] − b [ l − 1 ] ) − ( c [ r ] − c [ l − 1 ] ) b[r]*(b[r]-b[l-1])-(c[r]-c[l-1]) b[r](b[r]b[l1])(c[r]c[l1])

for(int l=1;l<=n;l++)
	for(int r=l;r<=n;r++)
		sum+=b[r]*(b[r]-b[l-1])-(c[r]-c[l-1]);

把最内层循环展开,是
b [ l ] ∗ ( b [ l ] − b [ l − 1 ] ) − ( c [ l ] − c [ l − 1 ] ) + b [ l + 1 ] ∗ ( b [ l + 1 ] − b [ l − 1 ] ) − ( c [ l + 1 ] − c [ l − 1 ] ) + . . . + b [ n ] ∗ ( b [ n ] − b [ l − 1 ] ) − ( c [ n ] − c [ l − 1 ] ) b[l]*(b[l]-b[l-1])-(c[l]-c[l-1])+b[l+1]*(b[l+1]-b[l-1])-(c[l+1]-c[l-1])+...+b[n]*(b[n]-b[l-1])-(c[n]-c[l-1]) b[l](b[l]b[l1])(c[l]c[l1])+b[l+1](b[l+1]b[l1])(c[l+1]c[l1])+...+b[n](b[n]b[l1])(c[n]c[l1])
拆开小括号,重新组合,得
( b [ l ] 2 + b [ l + 1 ] 2 + . . . + b [ n ] 2 ) − b [ l − 1 ] ∗ ( b [ l ] + b [ l + 1 ] + . . . + b [ n ] ) − ( c [ l ] + c [ l + 1 ] + . . . + c [ n ] ) + ( n − l + 1 ) ∗ c [ l − 1 ] (b[l]^2+b[l+1]^2+...+b[n]^2)-b[l-1]*(b[l]+b[l+1]+...+b[n])-(c[l]+c[l+1]+...+c[n])+(n-l+1)*c[l-1] (b[l]2+b[l+1]2+...+b[n]2)b[l1](b[l]+b[l+1]+...+b[n])(c[l]+c[l+1]+...+c[n])+(nl+1)c[l1]
我们求 b [ i ] 2 b[i]^2 b[i]2的前缀和,设为 d [ i ] d[i] d[i]
b [ i ] b[i] b[i]的前缀和,设为 e [ i ] e[i] e[i]
c [ i ] c[i] c[i]的前缀和,设为 f [ i ] f[i] f[i]

( d [ n ] − d [ l − 1 ] ) − b [ l − 1 ] ∗ ( e [ n ] − e [ l − 1 ] ) − ( f [ n ] − f [ l − 1 ] ) + ( n − l + 1 ) ∗ c [ l − 1 ] (d[n]-d[l-1])-b[l-1]*(e[n]-e[l-1])-(f[n]-f[l-1])+(n-l+1)*c[l-1] (d[n]d[l1])b[l1](e[n]e[l1])(f[n]f[l1])+(nl+1)c[l1]

for(int l=1;l<=n;l++)
	sum+=(d[n]-d[l-1])-b[l-1]*(e[n]-e[l-1])-(f[n]-f[l-1])+(n-l+1)*c[l-1];

现在复杂度就简化为 O ( n ) O(n) O(n)了。
接下来我们要对求余进行常规操作:
在求bcdef数组的时候,直接每求一个都取余,这个没所谓。
但是在sum里面,存在减法和乘法,事态就变得不同寻常了。
遇到减法之后,我们可以在减法进行的地方加mod,也可以在减法进行的地方做了乘法之后加mod,是一样的。
那么代码就写出来了。

代码:

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int maxn=2e5+4;
typedef long long ll;
ll a[maxn],b[maxn],c[maxn],d[maxn],e[maxn],f[maxn];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[i]=(b[i-1]+a[i])%mod;
		c[i]=(c[i-1]+a[i]*b[i-1])%mod;
		d[i]=(d[i-1]+b[i]*b[i])%mod;
		e[i]=(e[i-1]+b[i])%mod;
		f[i]=(f[i-1]+c[i])%mod;
	}
	ll ans=0;
	for(int l=1;l<=n;l++){
		ans+=(d[n]-d[l-1]-b[l-1]*(e[n]-e[l-1]))%mod+mod-(f[n]-f[l-1]+mod)+(n-l+1)*c[l-1];
		ans=(ans+mod)%mod;
	}
	printf("%lld\n",ans);
} 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值