[日常训练]挂科
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<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#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;j<i;++j)
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;
}