codeforces 938E MaxHistory 题解(概率or组合数学)

题目链接

题目大意

给你n个数字,求所有排列的fa的和:对于一个排列来说,一开始fa为1,m=1,若a[m]<a[i] fa=fa+a[m]。m=i

概率

看到这个方法觉得很神奇,但又确实高中的时候一些组合数学的题目的确可以用概率的方法来解。而且简单很多。首先sort一下,然后计算大于等于a[i]的个数x(包括自己),如果要保证a[i]被算进贡献,那么就必须在这个排列中,所有大于等于a[i]的个数中,a[i]在序列最前端,即第一个位置,显然概率就是1/x,然后有n!种方案。然后就可以求出答案

组合数学

第二种方法就有点难了。要涉及许多组合数学的问题。设有rk[i]个比a[i]小的数,显然这些数可以放在a[i]前面,而大于等于a[i]的数要放在a[i]后面。那么a[i]的贡献显然为
∑ j = 0 r k [ i ] A r k [ i ] j ∗ ( n − j − 1 ) ! \sum_{j=0}^{rk[i]}A_{rk[i]}^{j}*(n-j-1)! j=0rk[i]Ark[i]j(nj1)!化简为 r k [ i ] ! ∗ ∑ j = 0 r k [ i ] ( n − j − 1 ) ! ( r k [ i ] − j ) ! rk[i]!*\sum_{j=0}^{rk[i]}\frac{(n-j-1)!}{(rk[i]-j)!} rk[i]!j=0rk[i](rk[i]j)!(nj1)!而如果这么计算的话那么复杂度是线性的,再加上有n个a[i]那么复杂度就是 O ( n 2 ) O(n^2) O(n2),显然过不了,那么还要化简,首先你要明白一个公式
C k k + C k + 1 k + . . . . . + C n k = C n + 1 k + 1 C_{k}^{k}+C_{k+1}^{k}+.....+C_{n}^{k}=C_{n+1}^{k+1} Ckk+Ck+1k+.....+Cnk=Cn+1k+1
一直好奇这个式子怎么来的。。其实很简单就是一直运用pasacl公式
C n + 1 k + 1 = C n k + C n k + 1 = C n k + C n − 1 k + C n − 1 k + 1 = . . . . . . . = C n k + C n − 1 k + . . . . C k k C_{n+1}^{k+1}=C_{n}^{k}+C_{n}^{k+1}=C_{n}^{k}+C_{n-1}^{k}+C_{n-1}^{k+1}=.......=C_{n}^{k}+C_{n-1}^{k}+....C_{k}^{k} Cn+1k+1=Cnk+Cnk+1=Cnk+Cn1k+Cn1k+1=.......=Cnk+Cn1k+....Ckk

然后根据这个公式再度化简让分子分母同时乘以(n-rk[i]-1)!结果就变成 ( n − r k [ i ] − 1 ) ! ∗ r k [ i ] ! ∗ ∑ j = 0 r k [ i ] ( n − j − 1 ) ! ( r k [ i ] − j ) ! ∗ ( n − r k [ i ] − 1 ) ! (n-rk[i]-1)!*rk[i]!*\sum_{j=0}^{rk[i]}\frac{(n-j-1)!}{(rk[i]-j)!*(n-rk[i]-1)!} (nrk[i]1)!rk[i]!j=0rk[i](rk[i]j)!(nrk[i]1)!(nj1)!再度化简变成 ( n − r k [ i ] − 1 ) ! ∗ r k [ i ] ! ∗ ∑ j = 0 r k [ i ] C n − j − 1 n − r k [ i ] − 1 = ( n − r k [ i ] − 1 ) ! ∗ r k [ i ] ! ∗ C n n − r k [ i ] = n ! n − r k [ i ] (n-rk[i]-1)!*rk[i]!*\sum_{j=0}^{rk[i]}C_{n-j-1}^{n-rk[i]-1}=(n-rk[i]-1)!*rk[i]!*C_{n}^{n-rk[i]}=\frac{n!}{n-rk[i]} (nrk[i]1)!rk[i]!j=0rk[i]Cnj1nrk[i]1=(nrk[i]1)!rk[i]!Cnnrk[i]=nrk[i]n!
然后答案就变成了 ∑ i = 1 i = n a [ i ] ∗ n ! n − r k [ i ] \sum_{i=1}^{i=n}a[i]*\frac{n!}{n-rk[i]} i=1i=na[i]nrk[i]n!

两个方法显然推出来的式子一样

代码

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define debug printf("I am here\n");
using namespace std;
typedef long long ll;
const int maxn=1e6+5,mod=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-10;
int n,t,a[maxn],rk[maxn];
ll fac[maxn];
ll qpow(ll a,ll b){
    ll ans=1,base=a;
    while(b){
        if(b%2){
            ans=ans*base%mod;
        }
        base=base*base%mod;
        b=b>>1;
    }
    return ans;
}
void init(){
    fac[0]=1;
    for(int i=1;i<=1e6;i++){
        fac[i]=fac[i-1]*i%mod;
    }
}
int main(){
    init();
    scanf("%d",&n);i
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    sort(a+1,a+1+n);
    //rk[i]表示小于排序后的a[i]的个数
    for(int i=1;i<=n;i++){
        if(a[i]>a[i-1]){
            rk[i]=i-1;
        }else{//表示相等
            rk[i]=rk[i-1];
        }
    }
    ll sum=0;
    for(int i=1;i<=n;i++){
        if(rk[i]==rk[n]) break;//当a[i]等于最大显然要break,因为大于a[i]的个数为0个
        sum=(sum+1ll*a[i]*fac[n]%mod*qpow(n-rk[i],mod-2)%mod)%mod;
    }
    printf("%lld\n",sum);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值