牛客网暑期ACM多校训练营(第一场) F.Sum of Maximum(自然数幂次和)

28 篇文章 0 订阅
19 篇文章 0 订阅

链接:https://www.nowcoder.com/acm/contest/139/F
来源:牛客网

题目描述
Given a1, a2, …, an, find
这里写图片描述
modulo (109+7).
输入描述:
The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains an integer n.
The second line contains n integers a1, a2, …, an.
输出描述:
For each test case, print an integer which denotes the result.
示例1
输入

复制
2
1 2
5
2 3 3 3 3

输出

复制
3
453
备注:

  • 1 ≤ n ≤ 1000
  • 1 ≤ ai ≤ 109
  • The number of test cases does not exceed 10.

题意

给你n各数求出上述式子的值

思路


先思考n个数字相同的情况,即 ∑ x 1 i ∑ x 2 i ∑ x 3 i ⋯ ∑ x n i m a x ( x 1 , x 2 , x 3 , ⋯   , x n ) \sum_{x_1}^{i}\sum_{x_2}^{i}\sum_{x_3}^{i}\cdots \sum_{x_n}^{i}max(x_1,x_2,x_3,\cdots,x_n) x1ix2ix3ixnimax(x1,x2,x3,,xn)
如果我们知道了 1 ∼ i 1\sim i 1i中每个数字作为最大值出现的个数的话我们最后的答案就是
a n s = ∑ j = 1 i j ∗ n u m [ j ] ans=\sum_{j=1}^{i}j*num[j] ans=j=1ijnum[j]
对每个数最为最大值的数量打表会发现每个数为最大值的数量刚好与自然数有关有这样的关系
1 1 n 2 2 n − 1 n 3 3 n − ( 2 n − 1 ) − 1 n ⋮ ⋮ i i n − ( ( i − 1 ) n − ( i − 2 ) n − ⋯ − 1 n ) − ( ( i − 2 ) n − ⋯ − 1 n ) − ⋯ − 1 n \begin{matrix} 1 &1^n \\ 2 &2^n-1^n \\ 3 &3^n-(2^n-1)-1^n \\ \vdots & \vdots \\ i & i^n-((i-1)^n-(i-2)^n-\cdots -1^n)-((i-2)^n-\cdots -1^n)-\cdots -1^n \end{matrix} 123i1n2n1n3n(2n1)1nin((i1)n(i2)n1n)((i2)n1n)1n
我们设 n u m [ i ] num[i] num[i] i i i为最大值出现的数量,那么我们再记作 S [ i ] S[i] S[i]为出现的数的数量的和
即有 S [ i ] = n u m [ 1 ] + n u m [ 2 ] + ⋯ + n u m [ i ] = i n S[i]=num[1]+num[2]+\cdots+num[i]=i^n S[i]=num[1]+num[2]++num[i]=in
那么我们再看看原来的ans就可以变为
a n s = ∑ j = 1 i j ∗ n u m [ j ] = i ∗ S [ i ] − ∑ j = 1 i − 1 S [ j ] = i ∗ i n − ( 1 n + 2 n + 3 n + ⋯ + ( i − 1 ) n ) ans=\sum_{j=1}^{i}j*num[j]=i*S[i]-\sum_{j=1}^{i-1}S[j]=i*i^n-(1^n+2^n+3^n+\cdots+(i-1)^n) ans=j=1ijnum[j]=iS[i]j=1i1S[j]=iin(1n+2n+3n++(i1)n)


再回到原来的问题上,题目中给的n个是不一定相同的数,我们其实会发现答案和给的顺序没有关系,所以我们把数从小到大排序
a 1 a 2 a 3 ⋯ a n \begin{matrix} a_1 &a_2 &a_3 & \cdots &a_n \end{matrix} a1a2a3an
同样的我们也可以发现一个规律
1 1 n 2 2 n − 1 n 3 3 n − ( 2 n − 1 ) − 1 n ⋮ ⋮ a 1 a 1 n − ( ( a 1 − 1 ) n − ( a 1 − 2 ) n − ⋯ − 1 n ) − ( ( a 1 − 2 ) n − ⋯ − 1 n ) − ⋯ − 1 n a 1 + 1 a 1 ∗ ( a 1 + 1 ) n − 1 − S [ a 1 ] ⋮ ⋮ a 2 a 1 ∗ a 2 n − 1 − S [ a 2 − 1 ] ⋮ ⋮ a 3 a 1 ∗ a 2 ∗ a 3 n − 2 − S [ a 3 − 1 ] ⋮ ⋮ a n a 1 ∗ a 2 ∗ a 3 ∗ ⋯ ∗ a n − S [ a n − 1 ] \begin{matrix} 1 &1^n \\ 2 &2^n-1^n \\ 3 &3^n-(2^n-1)-1^n \\ \vdots & \vdots \\ a_1 & a_1^n-((a_1-1)^n-(a_1-2)^n-\cdots -1^n)-((a_1-2)^n-\cdots -1^n)-\cdots -1^n \\ a_1+1 & a_1*(a_1+1)^{n-1}-S[a_1]\\ \vdots & \vdots\\ a_2 & a_1*a_2^{n-1}-S[a_2-1]\\ \vdots & \vdots\\ a_3 & a_1*a_2*a_3^{n-2}-S[a_3-1]\\ \vdots & \vdots\\ a_n & a_1*a_2*a_3*\cdots*a_n-S[a_n-1] \end{matrix} 123a1a1+1a2a3an1n2n1n3n(2n1)1na1n((a11)n(a12)n1n)((a12)n1n)1na1(a1+1)n1S[a1]a1a2n1S[a21]a1a2a3n2S[a31]a1a2a3anS[an1]
同样的有 S [ a i ] = a 1 ∗ a 2 ∗ ⋯ ∗ a i n − i + 1 S[a_i]=a_1*a_2*\cdots*a_i^{n-i+1} S[ai]=a1a2aini+1,最后的答案和我们一开始的推出的形式是一样的即
a n s = ∑ j = 1 a n j ∗ n u m [ j ] = a n ∗ S [ a n ] − ∑ j = 1 a n − 1 S [ j ] ans=\sum_{j=1}^{a_n}j*num[j]=a_n*S[a_n]-\sum_{j=1}^{a_n-1}S[j] ans=j=1anjnum[j]=anS[an]j=1an1S[j]
现在要解决的问题就是如何快速求出 S [ j ] S[j] S[j]

我们可以分段求和处理 S [ 1 ] ∼ S [ a 1 ] S[1]\sim S[a_1] S[1]S[a1] S [ a 1 + 1 ] ∼ S [ a 2 ] S[a_1+1]\sim S[a_2] S[a1+1]S[a2] ⋯ \cdots S [ a n − 1 + 1 ] ∼ S [ a n ] S[a_{n-1}+1]\sim S[a_n] S[an1+1]S[an]

举个例子就可以理解了,如求解 S [ a i + 1 ] ∼ S [ a i + 1 ] S[a_i+1]\sim S[a_{i+1}] S[ai+1]S[ai+1]

S [ a i + 1 ] = a 1 ∗ a 2 ∗ ⋯ ∗ a i ∗ ( a i + 1 ) n − i , S [ a i + 2 ] = a 1 ∗ a 2 ∗ ⋯ ∗ a i ∗ ( a i + 2 ) n − i S[a_i+1]=a_1*a_2*\cdots*a_i*(a_{i}+1)^{n-i},S[a_i+2]=a_1*a_2*\cdots*a_i*(a_i+2)^{n-i} S[ai+1]=a1a2ai(ai+1)ni,S[ai+2]=a1a2ai(ai+2)ni
S [ a i + 3 ] = a 1 ∗ a 2 ∗ ⋯ ∗ a i ∗ ( a i + 3 ) n − i , ⋯   , S [ a i + 1 ] = a 1 ∗ a 2 ∗ ⋯ ∗ a i ∗ a i + 1 n − i S[a_i+3]=a_1*a_2*\cdots*a_i*(a_i+3)^{n-i},\cdots,S[a_{i+1}]=a_1*a_2*\cdots*a_i*a_{i+1}^{n-i} S[ai+3]=a1a2ai(ai+3)ni,,S[ai+1]=a1a2aiai+1ni
那么就有 ∑ S [ a i + 1 ] ∼ S [ a i + 1 ] \sum S[a_i+1]\sim S[a_{i+1}] S[ai+1]S[ai+1]
∑ S [ a i + 1 ] ∼ S [ a i + 1 ] = a 1 ∗ a 2 ∗ ⋯ ∗ a i ∗ ( ( a i + 1 ) n − i + ( a i + 2 ) n − i + ⋯ + a i + 1 n − i ) = a 1 ∗ a 2 ∗ ⋯ ∗ a i ∑ j = a i + 1 a i + 1 j n − i \sum S[a_i+1]\sim S[a_{i+1}]=a_1*a_2*\cdots*a_i*((a_{i}+1)^{n-i}+(a_{i}+2)^{n-i}+\cdots+a_{i+1}^{n-i})=a_1*a_2*\cdots*a_i\sum_{j=a_i+1}^{a_{i+1}}j^{n-i} S[ai+1]S[ai+1]=a1a2ai((ai+1)ni+(ai+2)ni++ai+1ni)=a1a2aij=ai+1ai+1jni
∑ S [ a i ] ∼ S [ a i + 1 ] \sum S[a_i]\sim S[a_{i+1}] S[ai]S[ai+1]可以用两个自然数幂次和相减来得到
∑ S [ a i + 1 ] ∼ S [ a i + 1 ] = a 1 ∗ a 2 ∗ ⋯ ∗ a i ( ∑ j = 1 a i + 1 j n − i − ∑ j = 1 a i j n − i ) \sum S[a_i+1]\sim S[a_{i+1}]=a_1*a_2*\cdots*a_i(\sum_{j=1}^{a_{i+1}}j^{n-i}-\sum_{j=1}^{a_{i}}j^{n-i}) S[ai+1]S[ai+1]=a1a2ai(j=1ai+1jnij=1aijni)
剩下的就是如何快速求出自然数次幂和了,有一个基于伯努利数的自然数求和公式
这里写图片描述
具体的实现可以参考伯努利数与自然数幂和
给出模版

typedef long long LL;
const LL mod = 1000000007;
const int N = 1005;

LL C[N][N];
LL B[N],Inv[N];
LL Tmp[N];
LL n;
void Init()
{
    //预处理组合数
    for(int i=0; i<N; i++)
    {
        C[i][0] = C[i][i] = 1;
        if(i == 0) continue;
        for(int j=1; j<i; j++)
            C[i][j] = (C[i-1][j] % mod + C[i-1][j-1] % mod) % mod;
    }
    //预处理逆元
    Inv[1] = 1;
    for(int i=2; i<N; i++)
        Inv[i] = (mod - mod / i) * Inv[mod % i] % mod;
    //预处理伯努利数
    B[0] = 1;
    for(int i=1; i<N; i++)
    {
        LL ans = 0;
        if(i == N - 1) break;
        for(int j=0; j<i; j++)
        {
            ans += C[i+1][j] * B[j];
            ans %= mod;
        }
        ans *= -Inv[i+1];
        ans = (ans % mod + mod) % mod;
        B[i] = ans;
    }
}

LL sum(int n,int k)
{
    Tmp[0] = 1;
    for(int i=1; i<N; i++)
        Tmp[i] = Tmp[i-1] * (n + 1) % mod;
    LL ans = Inv[k+1];
    LL sum = 0;
    for(int i=1; i<=k+1; i++)
    {
        sum += C[k+1][i] * Tmp[i] % mod * B[k+1-i] % mod;
        sum %= mod;
    }
    ans *= sum;
    ans %= mod;
    return ans;
}

所以我们就有代码

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL mod = 1000000007;
const int N = 1005;

LL C[N][N];
LL B[N],Inv[N];
LL Tmp[N];
LL n;

void Init()
{
    //预处理组合数
    for(int i=0; i<N; i++)
    {
        C[i][0] = C[i][i] = 1;
        if(i == 0) continue;
        for(int j=1; j<i; j++)
            C[i][j] = (C[i-1][j] % mod + C[i-1][j-1] % mod) % mod;
    }
    //预处理逆元
    Inv[1] = 1;
    for(int i=2; i<N; i++)
        Inv[i] = (mod - mod / i) * Inv[mod % i] % mod;
    //预处理伯努利数
    B[0] = 1;
    for(int i=1; i<N; i++)
    {
        LL ans = 0;
        if(i == N - 1) break;
        for(int j=0; j<i; j++)
        {
            ans += C[i+1][j] * B[j];
            ans %= mod;
        }
        ans *= -Inv[i+1];
        ans = (ans % mod + mod) % mod;
        B[i] = ans;
    }
}

LL sum(int n,int k)
{
    Tmp[0] = 1;
    for(int i=1; i<N; i++)
        Tmp[i] = Tmp[i-1] * (n + 1) % mod;
    LL ans = Inv[k+1];
    LL sum = 0;
    for(int i=1; i<=k+1; i++)
    {
        sum += C[k+1][i] * Tmp[i] % mod * B[k+1-i] % mod;
        sum %= mod;
    }
    ans *= sum;
    ans %= mod;
    return ans;
}
long long a[1005];
int main()
{
    Init();
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;i++)
            scanf("%lld",&a[i]);
        if(n==1)
        {
            printf("%lld\n",sum(a[0],1));
            continue;
        }
        sort(a,a+n);
        long long ans=a[n-1],s=1;
        for(int i=0;i<n;i++)
            ans=ans*a[i]%mod;
        for(int i=0;i<n;i++)
        {
            if(i==0)
            ans=(ans-s*(sum(a[i],n-i)))%mod;
            else if(a[i]!=a[n-1])
            {
                ans=(ans-s*(sum(a[i],n-i)-sum(a[i-1],n-i)))%mod;
            }
            else if(i==n-1&&a[i-1]!=a[n-1])
            {
                ans=(ans-s*(sum(a[i]-1,n-i)-sum(a[i-1],n-i)))%mod;
            }
             s=s*a[i]%mod;
        }
        printf("%lld\n",(ans+mod)%mod);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值