2017年四川省赛 --- L 题 (Nice Trick) 【前缀加暴力枚举】或【推公式】

补题地址

题目地址

思路 : 有两个方法, 一个是题目中已经给出三项式的计算方法, 这样我们可以通过枚举第四位来求出答案. 注意的是前三项那个计算式必须只能计算一次就可以知道所有的答案, 否则复杂度太高(On^2). 所以就用前缀和来计算(On). 二是 去理解三部分的计算方法怎么给出来的, 然后去推四部分的公式, 也可以做! 相对来说第二种方法时间更长, 数学功底要求大, 所以建议用第一种方法. 只要能想到用前缀和.
需要注意的两点: 涉及到了除法模所以需要用逆元(不懂的上网搜), 公式那部分有个减法, 所以可能减出来是负数, 需要把它变成正数.

第一种方法的AC 代码 (推荐!)
注意 : 一般两个数之间乘一下的时候最好就要mod一下, 因为范围是1e9, 如果不mod三个数相乘时就可能爆long long , 就会WA, 所以注意下!

#include 
#define ll long long int
using namespace std;
const ll M = 1e9+7;
const int maxn=1e5+5;
ll a[maxn];
ll ans1[maxn],ans2[maxn],ans3[maxn];

ll qpow(ll x, ll y)
{
    ll res = 1;
    while(y){
        if(y&1) res = res * x % M;
        x = x * x % M;
        y >>= 1;
    }
    return res % M;
}

ll cal(ll n)
{
    ll niyuan = qpow(6,M-2);  //因为有除法 %
    ll t1,t2,t3;
    t1 = ans1[n-1] ;
    t2 = ans2[n-1] ;
    t3 = ans3[n-1] ;
    ll s = (((t1 * t1) % M )* t1) % M - 3 * (t2 * t1 % M) % M + 2 * t3 % M;
    s = ((s % M ) + M ) % M;   //防止s为负数.
    s = s * niyuan % M ;
    return s % M;
}

int main()
{
    ll n;
    while(~scanf("%lld",&n)){
        for(int i=1;i<=n;i++){
            ans1[0] = ans2[0] = ans3[0] = 0;
            scanf("%lld",&a[i]);
            ans1[i] = ( ans1[i-1] + a[i] ) % M;
            //通过这样处理就可On的得到1到n的三部分计算所需要的数据了. 能想到这个就A这道题了.
            ans2[i] = (ans2[i-1] + (a[i]  * a[i]) % M ) % M;
            ans3[i] = (ans3[i-1] + ((a[i]  * a[i] % M ) % M * a[i] ) % M ) % M;
        }
        ll res = 0;
        for(int i = 4 ; i <= n ; i++){
            res = ( res + (cal(i) * a[i] % M ) ) % M ;
        }
        printf("%lld\n",res % M);
    }
}

第二种方法的AC 代码 (稍麻烦, 随意选择)

#include 
#define ll long long int
using namespace std;

const long long M = 1e9+7;
const int maxn=1e5+5;
ll a[maxn];

ll qpow(ll x, ll y)
{
    ll res = 1;
    while(y){
        if(y&1) res = res * x % M;
        x = x * x % M;
        y >>= 1;
    }
    return res;
}

ll qiu1ci(int n)
{
    ll res = 0;
    for(int i=0;ireturn res;
}
ll qiu2ci(int n)
{
    ll res = 0;
    for(int i=0;i2);
        res %= M;
    }
    return res;
}
ll qiu3ci(int n)
{
    ll res = 0;
    for(int i=0;i3);
        res %= M;
    }
    return res;
}
ll qiu4ci(int n)
{
    ll res = 0;
    for(int i=0;i4);
        res %= M;
    }
    return res;
}

int main(){
    ll n;
    while(~scanf("%lld",&n)){
        for(int i=0;iscanf("%lld",&a[i]);
        ll t1,t2,t3,t4,t5;
        t1 = qpow(qiu1ci(n),4) % M;
        t2 = (8*qiu3ci(n)%M * qiu1ci(n) ) % M;
        t3 = (3 * qiu2ci(n) * qiu2ci(n)) % M;
        t4 = (6 * qiu4ci(n) )% M;
        //最后推出来有四项, 前面的常数就是所推出来的公式的那一项的系数.
        t5 = ( 6 * qiu2ci(n) % M * qiu1ci(n) % M  * qiu1ci(n) % M) % M;   
        ll res = t1 + t2 + t3 - t4 - t5 ;
        res = ( res + M ) % M ;
        int niyuan = qpow(24,M -2 );
        res = res * niyuan % M;
        printf("%lld\n",res);
    }
}

你可能感兴趣的:(前缀和)