解析
依然不会亚qwq
但这次至少有点上道了
(指推出了一个会导致重复计数的错误式子)
首先,我们要选出碾压那些人,方案数就是Cn−1kC_{n-1}^kCn−1k
然后,我们要统计每门学科的排名情况
考虑比B神分数高的人一定是从没被碾压的人里选。所以对应的方案就是Cn−k−1ri−1C_{n-k-1}^{r_i-1}Cn−k−1ri−1,设为fkf_kfk
但是这样随便选可能会导致有的应该没被碾压的人一直没被选到,被碾压,所以这个东西其实是至少碾压kkk人的方案数
那么使用常规的容斥套路,这部分的答案就是fk−fk+1+fk+2−...f_k-f_{k+1}+f_{k+2}-...fk−fk+1+fk+2−...
最后,我们要统计每门学科的分数情况
设gu,a,bg_{u,a,b}gu,a,b表示总分数为u,B神前面有a个人,后面有b个人的方案数
枚举B神的分数 i,那么就有:
gu,a,b=∑i−1u(u−i)a∗ibg_{u,a,b}=\sum_{i-1}^u(u-i)^a*i^bgu,a,b=i−1∑u(u−i)a∗ib
但是这个东西暴力算是On的
(PS:我就是卡在这里了)qwq
考虑由于a和b非常少,我们可以使用离散化的思路
设n个人的分数一共有t个取值
枚举t,就有:
gu,a,b=∑t=1ngt,a,b×Cutg_{u,a,b}=\sum_{t=1}^ng_{t,a,b}\times C_u^tgu,a,b=t=1∑ngt,a,b×Cut
里面的 g 可以暴力求解
问题得以解决
代码
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
#define ll long long
#define il inline
il ll read(){
ll x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n,m,k;
ll c[105][105],u[105],r[105],mx=1000;
ll sum[105],jc[105],ni[105];
inline ll ksm(ll x,ll k){
ll res=1;
while(k){
if(k&1) res=res*x%mod;
x=x*x%mod;
k>>=1;
}
return res;
}
ll f[105][105];
ll D[105];
inline ll g(ll u,ll a,ll b){
ll res(0);
for(int i=1;i<=u;i++){
res+=f[u-i][a]*f[i][b];
res%=mod;
}
return res;
}
ll G(ll u,ll a,ll b){
ll C=1,ans=0;
//printf("\nG:u=%lld a=%lld b=%lld\n",u,a,b);
for(int t=1;t<=n;t++){
ll now=g(t,a,b);
for(int i=1;i<t;i++) now=(now-D[i]*c[t][i]%mod+mod)%mod;
D[t]=now;
C=C*(u-t+1)%mod*jc[t-1]%mod*ni[t]%mod;
ans+=now*C;
ans%=mod;
//printf(" t=%d C=%lld now=%lld ans=%lld\n",t,C,D[t],ans);
}
return ans;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();m=read();k=read();
for(int i=1;i<=m;i++) u[i]=read();
for(int i=1;i<=m;i++) r[i]=read(),mx=min(mx,n-r[i]);
jc[0]=1;
for(int i=1;i<=100;i++) jc[i]=jc[i-1]*i%mod;
ni[n]=ksm(jc[n],mod-2);
for(int i=n-1;i>=0;i--) ni[i]=ni[i+1]*(i+1)%mod;
for(int i=0;i<=100;i++){
f[i][0]=1;
for(int j=1;j<=100;j++) f[i][j]=f[i][j-1]*i%mod;
}
c[0][0]=1;
for(int i=1;i<=n;i++){
c[i][0]=1;
for(int j=1;j<=i;j++){
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
for(int i=1;i<=m;i++){
sum[i]=G(u[i],r[i]-1,n-r[i]);
//printf("i=%d sum=%lld\n",i,sum[i]);
}
ll ans=0;
for(int o=k;o<=n;o++){
ll now=c[n-k-1][n-o-1];
//printf(" i=0 now=%lld\n",now);
for(int i=1;i<=m;i++){
//now=now*c[n-o-1][r[i]-1]%mod;
now=now*c[n-o-1][r[i]-1]%mod*sum[i]%mod;
//printf(" i=%d now=%lld\n",i,now);
}
if((o-k)&1){
ans-=now;
if(ans<0) ans+=mod;
}
else{
ans+=now;
if(ans>=mod) ans-=mod;
}
//printf("o=%d now=%lld ans=%lld\n\n",o,now,ans);
//printf("o=%d now=%lld\n",o,now);
}
//for(int i=1;i<=m;i++){
//ans=ans*sum[i]%mod;
//printf("i=%d ans=%lld\n",i,ans);
//}
printf("%lld\n",ans*c[n-1][k]%mod);
return 0;
}
/*
*/

本文解析了一道涉及组合数学的编程竞赛题目,通过详细的步骤解释了如何计算特定条件下的排列组合数量,包括选择人员的方案数及学科分数的分布情况。
1082

被折叠的 条评论
为什么被折叠?



