[bzoj4559] [JLoi2016]成绩比较
Description
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神那么厉害,你只需要计算出情况数模1
0^9+7的余数就可以了。
Input
第一行包含三个正整数N,M,K,分别表示G系的同学数量(包括B神),必修课的数量和被B神碾压的同学数量。第二
行包含M个正整数,依次表示每门课的最高分Ui。第三行包含M个正整数,依次表示B神在每门课上的排名Ri。保证1
≤Ri≤N。数据保证至少有1种情况使得B神说的话成立。N<=100,M<=100,Ui<=10^9
Output
仅一行一个正整数,表示满足条件的情况数模10^9+7的余数。
Sample Input
3 2 1
2 2
1 2
Sample Output
10
题解
前置知识:容斥思想
此题其实可以分成两部分:
-
成绩相对关系的限制
-
成绩大小的限制
那么可以分成两部分考虑,然后乘起来即为最终答案。
相对关系的限制
考虑\(f(i)\)表示恰好有\(i\)个人被碾压的方案数,则可以对于每个科目进行考虑乘起来,然后减去不合法的,即:
式子前半部分考虑,当前剩下了\(n-i-1\)个人可以大于他,要从中选出\(rk(j)-1\)个人。
然后后半部分可以考虑,对于一个恰好有\(j\)个人的方案,会被算到\(\binom{j}{i}\)次,减掉就好了。
成绩大小的限制
同样可以对于每个科目分别计算。
枚举当前的成绩\(x\),然后大于和小于等于他的人都任选,在加起来,即:
\(lim\)为当前成绩上限。由于\(lim\)的范围非常大,这样算显然不行,我们二项式展开这个式子,整理下:
注意到后面一项为这样的形式:
观察下下面的式子:
然后将所有式子加起来,可以得到:
然后我们对于每个\(s\)大力算出\(g\),然后就做完了。
时间复杂度\(\Theta(n^3)\)。
#include<bits/stdc++.h>
using namespace std;
#define void inline void
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
const int mod = 1e9+7;
const int maxn = 200+10;
int n,m,k,lim[maxn],rk[maxn],f[maxn],ans,fac[maxn],ifac[maxn],g[maxn];
int binom(int x,int y) {return 1ll*fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
int qpow(int a,int x) {
int res=1;
for(;x;x>>=1,a=1ll*a*a%mod) if(x&1) res=1ll*res*a%mod;
return res;
}
void solve1() {
for(int i=n-1;i>=k;i--) {
int res=binom(n-1,i);
for(int j=1;j<=m;j++) res=(1ll*res*binom(n-i-1,rk[j]-1)%mod)%mod;
for(int j=i+1;j<=n-1;j++) res=(res-1ll*f[j]*binom(j,i)%mod)%mod;
f[i]=(res+mod)%mod;//write(f[i]);
}
}
void solve2() {
ans=1;
for(int s=1;s<=m;s++) {
int res=0;
g[0]=lim[s];
for(int j=1;j<=n+1;j++) {
g[j]=qpow(lim[s]+1,j+1)-1;
for(int i=0;i<j;i++) g[j]=(g[j]-1ll*binom(j+1,i)*g[i])%mod;
g[j]=1ll*g[j]*qpow(j+1,mod-2)%mod;
//printf("%d %d\n",j,g[j]);
}
for(int i=0,p=1;i<=rk[s]-1;i++) res=(res+1ll*binom(rk[s]-1,i)*qpow(lim[s],rk[s]-i-1)%mod*g[n-rk[s]+i]*p%mod)%mod,p=-p;
res=(res+mod)%mod;//write(res);
ans=1ll*ans*res%mod;
}
}
int main() {
read(n),read(m),read(k);fac[0]=ifac[0]=1;
for(int i=1;i<=200;i++) fac[i]=1ll*fac[i-1]*i%mod;
ifac[200]=qpow(fac[200],mod-2);
for(int i=200-1;i;i--) ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
for(int i=1;i<=m;i++) read(lim[i]);
for(int i=1;i<=m;i++) read(rk[i]);
solve1();solve2();
write(1ll*ans*f[k]%mod);
return 0;
}