CF938E-组合数

link:https://codeforces.com/contest/938/problem/E
题意:给一个序列 \(a\) ,按如下方式计算 \(f_a\)

  • 初始 \(f_a=0,M=1\)
  • 对每个 \(2\leq i\leq n\),如果 \(a_M<a_i\)\(f_a\to f_a+a_M\),然后 \(M=i\)
    对所有 \(a\) 的排列计算 \(f_a\) 并其在模 \(10^9+7\) 下的和。
    \(1\leq n\leq 10^6,1\leq a_i\leq 10^9\)

对每个序列的 \(f_a\) 的值是一段从 \(a_1\) 开始的上升序列的和,并且 \(a\) 的最大值没有贡献,反过来考虑每个 \(a_i<\max(a)\) 的贡献:

  • \(a_i\) 在位置 \(j\) 处出现,要产生贡献当且仅当前面的数严格比 \(a_i\) 小,假设这样的数有 \(s_i\) 个,则有 \(s_i!/(s_i-(j-1))!\) 种放法,后面的数随便放,有 \((n-j)!\) 种,$$\sum_{j=1}^n \frac{s_i!(n-j)!}{(s_i-j+1)!}=s_i!\sum_{j=1}^n \frac{(n-j)!}{(s_i-j+1)!}\frac{(n-1-s_i)!}{(n-1-s_i)!}=s_i!(n-1-s_i)! \sum_{j=1}^n \binom{n-j}{n-1-s_i}$$经典上指标求和,翻转指标,从\(\binom{n}{k}=\binom{n-1}{k}+\binom{n-1}{k-1}\)\(\binom{n}{-1}=0\),为 可以倒推:$$s_i!(n-1-s_i)!\sum_{j=0}^{n-1}\binom{j}{n-1-s_i}=s_i!(n-1-s_i)!\binom{n}{n-s_i}$$
  • 对每个值计算贡献即可
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const int N=1e6+55;
const int MOD=1e9+7;
int ksm(int a,int b){
    int ret=1;a%=MOD;
    for(;b;b>>=1,a=(ll)a*a%MOD)if(b&1)ret=(ll)ret*a%MOD;
    return ret;
}
int n,a[N],fact[N],inv_fact[N];
map<int,int> S;
int C(int n,int k){
    if(k>n)return 0;
    return (ll)fact[n]*inv_fact[k]%MOD*inv_fact[n-k]%MOD;
}
int main(){
    fastio;
    fact[0]=1;
    rep(i,1,N-5)fact[i]=(ll)fact[i-1]*i%MOD;
    inv_fact[N-5]=ksm(fact[N-5],MOD-2);
    for(int i=N-5;i>=1;i--)inv_fact[i-1]=(ll)inv_fact[i]*i%MOD;
    cin>>n;
    int mx=0;
    rep(i,1,n){
        cin>>a[i];
        S[a[i]]++;
        mx=max(mx,a[i]);
    }
    int cnt=0,ans=0;
    for(auto [x,c]:S)if(x!=mx){
        ans=(ans+(ll)x*fact[cnt]%MOD*fact[n-cnt-1]%MOD*C(n,n-cnt)%MOD*c%MOD)%MOD;
        cnt+=c;
    }
    cout<<ans;
    return 0;
}
posted @ 2024-03-21 12:09  yoshinow2001  阅读(8)  评论(1编辑  收藏  举报