[日常训练]挂科
Description
小$Y$非常喜欢学习,但却又经常翘课,现在期末来临,他要挂科了。
期末考中小$Y$一共考了$n$门课,这$n$门课的分数为$a_i$,而只要有一门课不及格,他就会留级。
现在小$Y$不知道每门课及格所要求的分数,他只知道他每一门课的分数都要严格高于这门课的要求分数。不过,小$Y$有个厉害的技能,那就是换成绩,具体地说,他可以将任意两门课的成绩互换,且他可以互换任意次。
现在他想知道,有多少种及格要求分数能不让他挂科(及格要求分数自然是非负整数)。
由于答案数可能很大,你只需要输出模$10^9+7$。
Input
第一行一个整数$T$,表示数据组数。
接下来$T$行每行一组数据。每组数据以一个整数$n$开头,表示课程数,后面$n$个整数$a_i$表示小$Y$这$n$门课的成绩。
Output
共$T$行,第$i$行表示第$i$组数据的答案。
Sample Input
4
2 1 2
3 2 5 3
4 1 2 3 4
4 9 8 3 5
Sample Output
3
74
125
4371
HINT
$1\;\leq\;n\;\leq\;1000,0\;\leq\;a_i\;\leq\;10^9,T\;\leq\;5$.
Solution
因为可以交换任意次,所以先将$a_i$从小到大排序.
$f[i]$表示处理到第$i$个数,前$i$个数合法的方案数.
$f[i]=a_i^i-\sum_{j=1}^{i-1}(f[j-1]\;\times\;(a_i-a_j)^{i-j+1}\;\times\;C_i^{j-1})$.
即总方案数-不合法的方案数.
枚举$j$,表示之前合法,在第$j$位不合法.之前合法的方案数为$f[j-1]$第$j$位到第$i$位不合法,只能填$[a_j,a_i)$,这种情况在前$i$位一共有$C_i^{j-1}$种分布.
#include#include #include #include #include #include #include #include #include #include #define N 1005 #define P 1000000007 #define M 1000000007ll using namespace std; typedef long long ll; ll fac[N],a[N],f[N],ans; int n,t; inline ll po(ll x,int k){ ll ret=1ll; while(k){ if(k&1) ret=ret*x%M; x=x*x%M;k>>=1; } return ret; } inline ll rev(ll x){ if(x==1ll) return 1ll; ll ret=1ll;int k=P-2; while(k){ if(k&1) ret=ret*x%M; x=x*x%M;k>>=1; } return ret; } inline ll c(int n,int m){ return fac[n]*rev(fac[n-m])%M*rev(fac[m])%M; } inline void Aireen(){ scanf("%d",&t); fac[0]=1ll; for(int i=1;i<=1000;++i) fac[i]=fac[i-1]*(ll)(i)%M; f[0]=1ll; while(t--){ scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%lld",&a[i]); sort(a+1,a+1+n); for(int i=1;i<=n;++i){ f[i]=po(a[i],i); for(int j=1;jj) f[i]=(f[i]-f[j-1]*po(a[i]-a[j],i-j+1)%M*c(i,j-1)%M+M)%M; } printf("%lld\n",f[n]); } } int main(){ freopen("godie.in","r",stdin); freopen("godie.out","w",stdout); Aireen(); fclose(stdin); fclose(stdout); return 0; }