JLOI 2016 成绩比较 题解

题目传送门

题目大意: n n n 个人(包括B神),有 m m m 门科目,第 i i i 门科目最高分为 U i U_i Ui,给出B神每门科目的分数排名,以及有 k k k 个人被B神碾压(即所有科目分数 ≤ \leq B神的),问有多少种分数方案?

题解

考虑先确定每个人的每门科目排名在B神前面还是后面,然后再确定具体分数。

由于碾压了 k k k 个人,则剩下的 n − k − 1 n-k-1 nk1 个每个至少有 1 1 1 科比他高。令 F i , j F_{i,j} Fi,j 表示这 n − k − 1 n-k-1 nk1 个人中,至多有 i i i 个人在第 j j j 门科目排名超过B神,则有 F i , j = ( i R j − 1 ) F_{i,j}=\binom i {R_j-1} Fi,j=(Rj1i)

容斥一下,得到 ( n − 1 k ) ∑ i = 0 n − k − 1 ( − 1 ) n − k − 1 − i ( n − k − 1 i ) ∏ j = 1 m F i , j \binom {n-1} k\sum_{i=0}^{n-k-1} (-1)^{n-k-1-i} \binom {n-k-1} i\prod_{j=1}^mF_{i,j} (kn1)i=0nk1(1)nk1i(ink1)j=1mFi,j,前面的 ( n − 1 k ) \binom {n-1} k (kn1) 是选出被碾压的 k k k 个人。

然后是确定每科的分数,考虑枚举B神的分数:
∏ i = 1 m ( ∑ j = 1 U i j n − R i ( U i − j ) R i − 1 ) = ∏ i = 1 m ( ∑ j = 1 U i j n − R i ∑ k = 0 R i − 1 ( R i − 1 k ) ( − 1 ) k j k U i R i − 1 − k ) = ∏ i = 1 m ( ∑ k = 0 R i − 1 ( − 1 ) k ( R i − 1 k ) U i R i − 1 − k ∑ j = 1 U i j n − R i j k ) = ∏ i = 1 m ( ∑ k = 0 R i − 1 ( − 1 ) k ( R i − 1 k ) U i R i − 1 − k ∑ j = 1 U i j n − R i + k ) \begin{aligned} &\prod_{i=1}^m\left(\sum_{j=1}^{U_i} j^{n-R_i}(U_i-j)^{R_i-1}\right)\\ &=\prod_{i=1}^m\left(\sum_{j=1}^{U_i} j^{n-R_i}\sum_{k=0}^{R_i-1}\binom {R_i-1} k (-1)^kj^kU_i^{R_i-1-k}\right)\\ &=\prod_{i=1}^m\left(\sum_{k=0}^{R_i-1} (-1)^k \binom {R_i-1} k U_i^{R_i-1-k} \sum_{j=1}^{U_i} j^{n-R_i}j^k\right)\\ &=\prod_{i=1}^m\left(\sum_{k=0}^{R_i-1} (-1)^k \binom {R_i-1} k U_i^{R_i-1-k} \sum_{j=1}^{U_i} j^{n-R_i+k}\right)\\ \end{aligned} i=1m(j=1UijnRi(Uij)Ri1)=i=1m(j=1UijnRik=0Ri1(kRi1)(1)kjkUiRi1k)=i=1m(k=0Ri1(1)k(kRi1)UiRi1kj=1UijnRijk)=i=1m(k=0Ri1(1)k(kRi1)UiRi1kj=1UijnRi+k)

后面是个自然数幂求和,套个拉格朗日插值就行了。最后把上下两部分乘起来就是答案。时间复杂度 O ( n 2 m ) O(n^2m) O(n2m)

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 310
#define mod 1000000007

int n,m,k,U[maxn],R[maxn];
int fac[maxn],inv[maxn],inv_fac[maxn];
void FacInit(){
    
    
	fac[0]=inv_fac[0]=inv[1]=1;
	for(int i=1;i<=maxn-10;i++)fac[i]=1ll*fac[i-1]*i%mod;
	for(int i=2;i<=maxn-10;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<=maxn-10;i++)inv_fac[i]=1ll*inv_fac[i-1]*inv[i]%mod;
}
int Binom(int x,int y){
    
    
	if(x<y||y<0)return 0;
	return 1ll*fac[x]*inv_fac[y]%mod*inv_fac[x-y]%mod;
}
int ans1=0,ans2=1;
void add(int &x,int y){
    
    x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){
    
    x=(x-y<0?x-y+mod:x-y);}
void solve1(){
    
    
	for(int i=0;i<=n-k-1;i++){
    
    
		int prod=Binom(n-k-1,i);
		for(int j=1;j<=m;j++)
			prod=1ll*prod*Binom(i,R[j]-1)%mod;
		(n-k-1-i&1?dec:add)(ans1,prod);
	}
	ans1=1ll*ans1*Binom(n-1,k)%mod;
}
int ksm(int x,int y){
    
    int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
struct Lagrange{
    
    
	Lagrange(){
    
    }
	int pre[maxn],suf[maxn];
	int calc(int x,int k){
    
    
		if(x<=k+2){
    
    
			int re=0;
			for(int i=1;i<=x;i++)
				add(re,ksm(i,k));
			return re;
		}else{
    
    
			int re=0,sum=0;
			pre[0]=1;for(int i=1;i<=k+2;i++)pre[i]=1ll*pre[i-1]*(x-i)%mod;
			suf[k+3]=1;for(int i=k+2;i>=1;i--)suf[i]=1ll*suf[i+1]*(x-i)%mod;
			for(int i=1;i<=k+2;i++){
    
    
				add(sum,ksm(i,k));
				(k+2-i&1?dec:add)(re,1ll*sum*pre[i-1]%mod*suf[i+1]%mod*inv_fac[i-1]%mod*inv_fac[k+2-i]%mod);
			}
			return re;
		}
	}
}Lag;
void solve2(){
    
    
	for(int i=1;i<=m;i++){
    
    
		int tot=0;
		for(int p=0;p<=R[i]-1;p++)
			(p&1?dec:add)(tot,1ll*Binom(R[i]-1,p)*ksm(U[i],R[i]-1-p)%mod*Lag.calc(U[i],n-R[i]+p)%mod);
		ans2=1ll*ans2*tot%mod;
	}
}

int main()
{
    
    
	scanf("%d %d %d",&n,&m,&k);FacInit();
	for(int i=1;i<=m;i++)scanf("%d",&U[i]);
	for(int i=1;i<=m;i++)scanf("%d",&R[i]);
	solve1();solve2();printf("%lld",1ll*ans1*ans2%mod);
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/112621327