题目链接:https://ac.nowcoder.com/acm/contest/1110/L
题解:
由于题目给了选三个数乘积求和的公式,所以我刚开始想怎么推选四个数乘积求和的公式,然后队友说枚举其中一个数字就可以套题目给的那个公式了,想了想确实是,先预处理一下前缀和,然后枚举al, 对于前边的ai*aj*ak的和,可以O(1)进行计算。
比赛结束之后发现有dp写法,更简单一点。
下边给出两个代码:
dp代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int mod=1e9+7;
ll f[maxn][4];
int n;
int main(){
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
f[i][1]=(f[i-1][1]+x)%mod;
for(int j=2;j<=4;j++){
f[i][j]=(f[i-1][j]+f[i-1][j-1]*x)%mod;
}
}
printf("%lld\n",f[n][4]);
}
return 0;
}
前缀和+枚举代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+5;
ll x[maxn];
ll a[maxn];
ll b[maxn];
ll c[maxn];
const ll ni=166666668; //6的逆元
ll calc(int i){
ll s=a[i]*a[i]%mod*a[i]%mod;
ll t=3*b[i]*a[i]%mod;
ll w=2*c[i]%mod;
ll ans=(s-t+w+mod)%mod;
ans=ans*ni%mod;
return ans;
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++){
scanf("%lld",&x[i]);
a[i]=x[i];
b[i]=a[i]*a[i]%mod;
c[i]=b[i]*a[i]%mod;
a[i]=(a[i]+a[i-1])%mod;
b[i]=(b[i]+b[i-1])%mod;
c[i]=(c[i]+c[i-1])%mod;
}
ll ans=0;
for(int i=4;i<=n;i++){
ans=(ans+x[i]*calc(i-1))%mod;
}
printf("%lld\n",ans);
}
return 0;
}