【LuoguP3270】[JLOI2016] 成績比較

題目鏈接

題目描述

G系共有n位同學,M門必修課。這N位同學的編號爲0到N-1的整數,其中B神的編號爲0號。這M門必修課編號爲0到M-1的整數。一位同學在必修課上可以獲得的分數是1到Ui中的一個整數。

如果在每門課上A獲得的成績均小於等於B獲得的成績,則稱A被B碾壓。在B神的說法中,G系共有K位同學被他碾壓(不包括他自己),而其他N-K-1位同學則沒有被他碾壓。D神查到了B神每門必修課的排名。

這裏的排名是指:如果B神某門課的排名爲R,則表示有且僅有R-1位同學這門課的分數大於B神的分數,有且僅有N-R位同學這門課的分數小於等於B神(不包括他自己)。

我們需要求出全系所有同學每門必修課得分的情況數,使其既能滿足B神的說法,也能符合D神查到的排名。這裏兩種情況不同當且僅當有任意一位同學在任意一門課上獲得的分數不同。

你不需要像D神那麼厲害,你只需要計算出情況數模10^9+7的餘數就可以了。

Sol

量比較多,且初步看上去合併很困難,那麼我們就必然從容斥的角度入手來簡化問題了。

首先我們注意到有一個恰好有 K 個人被 B 君完虐的條件,這個條件使得學科之間有了聯繫,因爲我們必須保證某些成績低於 B 君的分數都落在某一個人身上,而我們如果僅僅只需考慮一門學科的話是很好算的。所以我們把這個條件容斥掉,那麼就二項式反演列出初步的式子:
i=kn1(1)ik(ik)(n1i)j=1mF[i][j]\sum_{i=k}^{n-1} (-1)^{i-k}{i\choose k}{n-1\choose i}\prod_{j=1}^m F[i][j]

先枚舉總共至少上有多少個人被B君完虐,前面的一個組合數就是二項式反演的容斥係數,後面的組合數則是在除B君外的 n1n-1 個人裏挑出 ii 個被完虐。之後各個學科之間就互不影響了,方案數直接乘起來。
那麼後面的方案數 F[i][j]F[i][j] 是什麼呢,就是對於一門學科 jj 而言,現在強制有了至少 ii 個人被完虐,使得排名合法的方案數。
這個的式子是比較輕鬆就可以列出的。
F[i][j]=x=1UjxnR[j](U[j]x)R[j]1(n1iR[j]1)F[i][j]=\sum_{x=1}^{U_j} x^{n-R[j]}*(U[j]-x)^{R[j]-1}*{n-1-i\choose R[j]-1}

直接枚舉 B 君這一門的分數 ,我們可以確定有多少個人的分數小於等於他和大於他,並且枚舉了分數之後這些人的分數範圍也出來了。後面乘上一個組合數是因爲除了強制的被完虐的人已經給 B 君墊着以外,爲了保證B君在這一們學科上的排名,我們還需要額外選出一些人給他墊着。

發現這樣子其實中間一大坨東西都和 i 沒有關係,我們直接把中間的處理出來就可以了,那麼就看怎麼算中間的這個東西了:

x=1UjxnR[j](U[j]x)R[j]1\sum_{x=1}^{U_j} x^{n-R[j]}*(U[j]-x)^{R[j]-1}

直接暴力二項式定理展開然後交換求和順序就好了,最後的式子長這樣:

t=0R[j]1(1)t(R[j]1t)U+jR[j]1tx=1UjxnR[j]+t\sum_{t=0}^{R[j]-1}(-1)^t{R[j]-1\choose t}U+j^{R[j]-1-t}*\sum_{x=1}^{U_j}x^{n-R[j]+t}

後面那玩意就是自然指數冪了,隨便用一種方法就行了。(所以我現在做的拉格朗日插值的題目怎麼都是求自然指數冪以及他們的前綴和啊)

code:

#include<bits/stdc++.h>
using namespace std;
template<class T>inline void init(T&x){
	x=0;char ch=getchar();bool t=0;
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	if(t) x=-x;return;
}
typedef long long ll;
int n,m,k;
const int N=112;
const int mod=1e9+7;
int U[N],R[N],fac[N],finv[N];
const int inv2=500000004;
template<class T>inline void Inc(T&x,int y){x+=y;if(x>=mod) x-=mod;}
template<class T>inline void Dec(T&x,int y){x-=y;if(x < 0 ) x+=mod;}
template<class T>inline int fpow(int x,T k){int ret=1;for(;k;k>>=1,x=(ll)x*x%mod) if(k&1) ret=(ll)ret*x%mod;return ret;}
inline int Sum(int x,int y){x+=y;return x>=mod? (x-mod):x;}
inline int Dif(int x,int y){x-=y;return x < 0 ? (x+mod):x;}
inline int C(int n,int m){return n<m? 0:((ll)fac[n]*finv[m]%mod*finv[n-m]%mod);}
int F[N],S[N];
inline void Prework(){
	int m=110;
	fac[0]=finv[0]=1;
	for(register int i=1;i<=m;++i) fac[i]=1ll*fac[i-1]*i%mod;
	finv[m]=fpow(fac[m],mod-2);
	for(register int i=m-1;i;--i) finv[i]=1ll*finv[i+1]*(i+1)%mod;
	return;
}
int Y[N];
inline int Lagerange(int K,int n){
	int LIM=K+2;int S=1;
	for(int i=1;i<=LIM;++i) {S=(ll)S*Dif(n,i)%mod;Y[i]=Sum(Y[i-1],fpow(i,K));}
	if(n<=LIM) return Y[n];
	int ans=0;
	for(int i=1;i<=LIM;++i) {
		int ret=(ll)S*fpow(Dif(n,i),mod-2)%mod*finv[i-1]%mod*finv[LIM-i]%mod;
		if((LIM-i)&1) ret=mod-ret;
		Inc(ans,(ll)Y[i]*ret%mod);
	}
	return ans;
}
int main()
{
	init(n),init(m),init(k);
	int Mx=0;Prework();
	for(register int i=1;i<=m;++i) init(U[i]);
	for(register int i=1;i<=m;++i) init(R[i]),Mx=max(Mx,R[i]);
	--Mx;int ans=0;
	for(int i=1;i<=m;++i) {
		F[i]=0;
		int inv=fpow(U[i],mod-2);
		int G=fpow(U[i],R[i]);
		for(int t=0;t<R[i];++t){
			G=(ll)G*inv%mod;
			int ret=(ll)C(R[i]-1,t)*G%mod*Lagerange(n-R[i]+t,U[i])%mod;
			(t&1)? Dec(F[i],ret):Inc(F[i],ret);
		}
	}
	for(int i=k;i<=n;++i) {
		if(n-1-i<Mx) break;
		int ret=(ll)C(i,k)*C(n-1,i)%mod;
		int base=1;
		for(int j=1;j<=m;++j) base=(ll)base*C(n-1-i,R[j]-1)%mod*F[j]%mod;
		((i-k)&1)? Dec(ans,(ll)ret*base%mod):Inc(ans,(ll)ret*base%mod);
	}
	cout<<Sum(ans,mod)%mod<<endl;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章