记 \(a_i=N-R_i,n=N-1\)。
先不考虑有多少人被碾压,计算出符合排名限制的方案数。枚举每门课和 B 神在每门课中的得分,选出 \(a_i\) 个人得分小于等于他,即:
\[\prod\limits_{i=1}^m \dbinom{n}{a_i} \sum\limits_{j=1}^{U_i} j^{a_i}(U_i-j)^{n-a_i} \]设 \(s(x)=\sum\limits_{j=1}^{x} j^{a_i}(U_i-j)^{n-a_i}\),是关于 \(x\) 的 \(n\) 次多项式,直接拉格朗日插值求出 \(s(U_i)\) 即可。
接下来计算出恰好有 \(k\) 个人被碾压的概率。设 \(f_{i,j}\) 表示仅考虑前 \(i\) 门课,恰好有 \(j\) 个人被碾压的概率。初始状态 \(f_{0,n}=1\),根据组合意义可得转移方程为:
\[f_{i,j}=\sum\limits_{p=j}^{n+j-a_i} f_{i-1,p}\times\dfrac{\tbinom{p}{j}\tbinom{n-p}{a_i-j}}{\tbinom{n}{a_i}} \]最终答案即为 \(f_{n,k}\times \prod\limits_{i=1}^{m} s(U_i)\),时间复杂度 \(O(n^2m)\)。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=110,mod=1e9+7;
int n,m,k,U[N],a[N],s[N];
ll ans=1,P[N],I[N],f[N],g[N],dp[N][N];
inline ll ksm(ll a,int b=mod-2)
{
ll ans=1;
for(;b;b>>=1,a=a*a%mod)
if(b&1) ans=ans*a%mod;
return ans;
}
inline ll C(int n,int m)
{return P[n]*I[m]%mod*I[n-m]%mod;}
inline ll work(int m,int k)
{
for(int i=1;i<=min(m,n+1);++i)
s[i]=(ksm(i,k)*ksm(m-i,n-k)+s[i-1])%mod;
if(m<=n+1) return s[m];
f[0]=m,g[n+2]=1;ll ans=0;
for(int i=1;i<=n+1;++i) f[i]=(m-i)*f[i-1]%mod;
for(int i=n+1;~i;--i) g[i]=(m-i)*g[i+1]%mod;
for(int i=1;i<=n+1;++i)
ans+=I[i]*I[n+1-i]%mod*f[i-1]%mod*g[i+1]%mod*s[i]%mod*(((n+1-i)&1)?-1:1);
return (ans%mod+mod)%mod;
}
signed main()
{
cin>>n>>m>>k;
P[0]=1;for(int i=1;i<=n;++i) P[i]=P[i-1]*i%mod;
I[n]=ksm(P[n]);for(int i=n;i;--i) I[i-1]=I[i]*i%mod;
--n,dp[0][n]=1;for(int i=1;i<=m;++i) cin>>U[i];
for(int i=1;i<=m;++i) cin>>a[i],a[i]=n-(a[i]-1);
for(int i=1;i<=m;++i)
ans=ans*C(n,a[i])%mod*work(U[i],a[i])%mod;
for(int i=1;i<=m;++i) for(int j=k;j<=a[i];++j)
{
for(int p=j;p<=n+j-a[i];++p)
dp[i][j]+=C(p,j)*C(n-p,a[i]-j)%mod*dp[i-1][p]%mod;
dp[i][j]=dp[i][j]%mod*ksm(C(n,a[i]))%mod;
}
cout<<ans*dp[m][k]%mod<<'\n';
}
标签:JLOI2016,limits,P3270,int,ll,ans,成绩,sum,mod
From: https://www.cnblogs.com/int-R/p/18206340/P3270