Link
Solution
考虑每个元素的贡献。该元素对答案有贡献当且仅当前面没有比该元素大的数,且后面存在比该元素大的数。容易想到先将序列排序。
假设所有元素都不同,当前是第 (i) 个,枚举该元素前面的数的个数,然后计数
[sum_{k=0}^{i-1} k!(n-k-1)!inom{i-1}{k}
]
然后就会发现 (k!(n-k-1)!) 是个常数列,记为 (f_k)。然后就会发现上面的式子是 (f) 和 (1) 的 egf 的二项卷积。NTT 一下就做完了。(但是模数是 1e9+7,这种方法并不可取)
还是太年轻了。
由于 (f_k) 这个东西很特殊,所以应该先考虑化简。注意到有
[k!(n-k-1)!=frac{k!(n-k-1)!(n-1)!}{(n-1)!}=(n-1)!inom{n-1}{k}^{-1}
]
所以就化成
[(n-1)! sum_{i=0}^{i-1} inom{i-1}{k}Big/inom{n-1}{k}
]
然后你就会发现这个和《具体数学》5.2 第一个例题长得差不多,大概就是考虑运用三项恒等式后再上指标求和。
最后便是
[(i-1)!(n-i)!inom{n}{i-1}
]
然后再看有重复元素的。容易发现必须是第一个该值的元素才会造成贡献,所以只需要把所有同值元素按照第一个遇到的该种元素处理即可。
#include<stdio.h>
#include<algorithm>
using namespace std;
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
typedef long long ll;
const int N=1e6+7;
const int Mod=1e9+7;
int qpow(ll x,int y=Mod-2){
ll ret=1;
while(y){
if(y&1) ret=ret*x%Mod;
x=x*x%Mod,y>>=1;
}
return ret;
}
ll fac[N],a[N],inv[N];
ll C(int x,int y){return x<y? 0:fac[x]*inv[y]%Mod*inv[x-y]%Mod;}
int main(){
int n=read(); fac[0]=1;
for(int i=1;i<=n;i++)
a[i]=read(),fac[i]=fac[i-1]*i%Mod;
inv[n]=qpow(fac[n]);
for(int i=n-1;~i;i--)
inv[i]=inv[i+1]*(i+1)%Mod;
sort(a+1,a+1+n); int mx=a[n];
ll ans=0,q=0;
for(int i=1;i<=n;i++){
if(a[i]==mx) break;
if(a[i]!=a[i-1]) q=fac[i-1]*fac[n-i]%Mod*C(n,i-1)%Mod;
ans=(ans+a[i]*q%Mod)%Mod;
}
printf("%lld",ans);
}