传送门
唉最开始居然把题给看错了。
其实是组合数学傻逼题呢。
题意简述:给出一个数列,定义一个与数列有关的
f
f
f函数,
f
f
f函数定义如下:
首先
f
=
0
,
M
=
1
f=0,M=1
f=0,M=1,一直重复如下操作:在
2
2
2~
n
n
n中找到第一个比
a
M
a_M
aM大的
a
i
a_i
ai,然后
f
+
=
a
M
,
M
=
i
f+=a_M,M=i
f+=aM,M=i
求该数列
n
!
n!
n!个排列的
f
f
f函数之和。
这题一看就是统计每个数对答案的贡献次数。
具体说说就是看每个数在哪些排列中能有贡献。
于是考虑
a
i
a_i
ai的贡献。
设严格小于
a
i
a_i
ai的有
l
l
l个。
所以除了
a
i
a_i
ai和那
l
l
l个数之外的数必须全部在
a
i
a_i
ai之后出现才能使
a
i
a_i
ai有贡献,这些数的排列种类数等于
(
n
−
l
−
1
)
!
(n-l-1)!
(n−l−1)!
然后考虑剩下的
l
l
l个数。
自身有
l
!
l!
l!种排列方式,考虑如何插空。
显然可以先把这
l
l
l个数位置选出来再安排剩下的。
于是
a
n
s
=
(
n
l
)
∗
l
!
∗
(
n
−
l
−
1
)
!
=
n
!
n
−
l
ans=\binom n l*l!*(n-l-1)!=\frac{n!}{n-l}
ans=(ln)∗l!∗(n−l−1)!=n−ln!
然后对每个数都统计一遍就行了。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int N=1e6+5,mod=1e9+7;
typedef long long ll;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int n,a[N],ans=0,fac[N],b[N],sig=0,siz[N];
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=(ll)a*a%mod)if(p&1)ret=(ll)ret*a%mod;return ret;}
inline int calc(int i){return (ll)fac[n-siz[i-1]-1]*ksm(n-siz[i-1],mod-2)%mod;}
int main(){
n=read(),fac[0]=1;
for(ri i=1;i<=n;++i)a[i]=read(),fac[i]=(ll)fac[i-1]*i%mod;
sort(a+1,a+n+1);
for(ri i=1;i<=n;++i)b[i]=a[i];
sig=unique(b+1,b+n+1)-b-1;
for(ri i=1,j=1;i<=n;++i){if(a[i]^b[j])++j;++siz[j];}
for(ri i=1;i<sig;++i)siz[i]+=siz[i-1],(ans+=(ll)(siz[i]-siz[i-1])*b[i]%mod*ksm(n-siz[i-1],mod-2)%mod)%=mod;
cout<<(ll)ans*fac[n]%mod;
return 0;
}