欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-07 19:09:03
...

序列卷积之和

传送门
题意:求l=1nr=lni=lrj=irai×ajmod  1e9+7\sum_{l=1}^n\sum_{r=l}^n\sum_{i=l}^r\sum_{j=i}^ra_i\times a_j\mod 1e9+7
[ACM]【prefix/求和/取余】牛客练习赛64 序列卷积之和

思路:

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

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+1]+...+a[r])a[i]*(a[i]+a[i+1]+...+a[r])
我们求a[i]a[i]的前缀和,设为b[i]b[i]

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

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[l1])+a[l+1](b[r]b[l+11])+...+a[r](b[r]b[r1])a[l]*(b[r]-b[l-1])+a[l+1]*(b[r]-b[l+1-1])+...+a[r]*(b[r]-b[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]*(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](b[r]b[l1])(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])
我们求a[i]b[i1]a[i]*b[i-1]的前缀和,设为c[i]c[i]

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

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[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]*(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]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[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[i]2b[i]^2的前缀和,设为d[i]d[i]
b[i]b[i]的前缀和,设为e[i]e[i]
c[i]c[i]的前缀和,设为f[i]f[i]

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

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)了。
接下来我们要对求余进行常规操作:
在求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);
} 
相关标签: 数学 基础