cogs 2752. [济南集训 2017] 数列运算

2752. [济南集训 2017] 数列运算

★★☆   输入文件:sequenceQBXT.in   输出文件:sequenceQBXT.out   简单对比
时间限制:1 s   内存限制:512 MB

【题目描述】

在纸上有一个长为n的数列,第i项值为ai

现在小A想要在这些数之间添加加号或乘号。问对于不同的2n1种方案,

所有答案的和是多少?

由于数据范围较大,所以输出对1000000007取模的结果。

【输入格式】

输入第一行一个整数n表示数列的长度。

之后一行n个整数,第n个整数表示数列的第iai

【输出格式】

m行,第i行表示第i个询问的答案对1000000007取模的结果。

【样例输入】

3
1 2 4

【样例输出】

30

【数据范围与约定】

 

对于30%的数据,1n10,1ai105

对于另外30%的数据,1n1000,ai=1

对于90%的数据,1n1000,1ai105

对于100%的数据,1100000,1ai109

【来源】

清北学堂济南NOIP集训二试T2

思路:

一个数列,长为n

在里面加n-1个加号或乘号

问方案所有方案的数列和

有部分数据数列全为1

那么枚举有i个乘号,ans=Σ(C(n-1,2)*(n-i))

一、基本思路O(n^3)

dp[i]表示到第i个数的答案

枚举前一个加号在j后面

那么到j一共有2^(j-1)种方案,每种方案在j后面都是一个加号,加号后面全是乘号

令tot=a[j+1]*a[j+2]……*a[i]

dp[i]=Σ(dp[j]+2^(j-1)*tot)

#include<cstdio>
#include<cstring>
#define mod 1000000007
using namespace std;
int n,a[100001];
long long cf[100001];
long long dp[100001],pre[100001],inv[100001];
long long pow(long long a,long long b){
    long long res=1;
    while(b){
        if(b&1)
            res=res*a%mod;
        a=a*a%mod;
        b>>=1; 
    }
    return res;
}
int main(){
    freopen("sequenceQBXT.in","r",stdin);
    freopen("sequenceQBXT.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)     scanf("%d",&a[i]);
    cf[0]=1; 
    for(int i=1;i<=n;i++) cf[i]=cf[i-1]*2%mod;
    long long tot;
    pre[0]=1;
    for(int i=1;i<=n;i++) pre[i]=pre[i-1]*a[i]%mod;
    for(int i=1;i<=n;i++) inv[i]=pow(pre[i],mod-2);
    long long sum1=0,sum2=1;
    for(int i=1;i<=n;i++){
        dp[i]=(sum1+sum2*pre[i]%mod)%mod;
        sum1=(sum1+dp[i])%mod;
        sum2=(sum2+cf[i-1]%mod*inv[i]%mod)%mod;
    } 
    printf("%I64d",dp[n]);    
}

 

posted @ 2017-09-13 15:15  一蓑烟雨任生平  阅读(164)  评论(0编辑  收藏  举报