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

链接: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 )

如果我们知道了 1 i 中每个数字作为最大值出现的个数的话我们最后的答案就是
a n s = j = 1 i j n u m [ 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

我们设 n u m [ i ] i 为最大值出现的数量,那么我们再记作 S [ i ] 为出现的数的数量的和
即有
S [ i ] = n u m [ 1 ] + n u m [ 2 ] + + n u m [ i ] = i n

那么我们再看看原来的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 )


再回到原来的问题上,题目中给的n个是不一定相同的数,我们其实会发现答案和给的顺序没有关系,所以我们把数从小到大排序

a 1 a 2 a 3 a n

同样的我们也可以发现一个规律
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 ]

同样的有 S [ a i ] = a 1 a 2 a i n i + 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 ]

现在要解决的问题就是如何快速求出 S [ j ]

我们可以分段求和处理 S [ 1 ] S [ a 1 ] S [ a 1 + 1 ] S [ a 2 ] S [ a n 1 + 1 ] S [ a n ]

举个例子就可以理解了,如求解 S [ a i + 1 ] S [ a i + 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 + 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 + 1 ] S [ a i + 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

S [ a i ] S [ a i + 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 )

剩下的就是如何快速求出自然数次幂和了,有一个基于伯努利数的自然数求和公式
这里写图片描述
具体的实现可以参考 伯努利数与自然数幂和
给出模版

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;
}

猜你喜欢

转载自blog.csdn.net/ftx456789/article/details/81170189