题意:
有n个人,m门课,每个人在每门课的得分是一个$[1,u_i ]$之间的整数。
你知道自己在每门课的排名$r_i$,即有$r_i-1$个人得分高于你,$n-r_i$个人得分不高于你(不含自己)。
求你恰好碾压k个人的方案数,a碾压b的含义为a每门课的得分都不低于b的得分。
$n,mleq 100,u_i leq 10^{9}$。
题解:
一看到恰好k个就想到一个套路:$Num(恰好k个)=Num(钦定k个)-Num(钦定k+1个)+cdots$。
此时我们要考虑到一个严重的问题:(以下为防止组合数越界有做简单变式)
这题从n-1个人里选x个被碾压的方案数到底是${n-1}choose {n-1-x}$还是${{n-1} choose {n-1-k}}{{n-1-k}choose {n-1-x}}$?
首先回顾一下${n}choose x$和${{n} choose m}{mchoose x}$的本质区别:前者是分两个集合,后者是分三个集合。
也就是说对于每个大小为x的集合S,前者只被算了一遍,后者被算了${n-x}choose{m-x}$遍。
那么我们考虑这道题容斥的底层过程:有两个大小为k的集合S1和S2,它们的并集是大小为k+1的S3。
那S3是被算了一遍还是被算了${{k+1}choose{k}}$遍呢?显然是后者,于是一开始就应该用后者计算。
(upd:另一种解释:是对于每个集合容斥而不是对于集合大小容斥,所以每次必须钦定k个不动。)
回到容斥,答案就是
${n-1choose {n-1-k}}sum limits_{j=k}^{n-1}{(-1)^{j-k}{n-1-kchoose {n-1-j}}prod limits_{i=1}^{m}{n-1-jchoose n-r_i -j}sum limits_{x=1}^{u_i}{x^{n-r_i}(u_i -x)^{r_i -1}}}$
最后面那个式子可以用二项式定理拆一下,我是在容斥里面写了个子容斥。(所以说我nt呢)
大概形如
${n-1choose {n-1-k}}sum limits_{j=k}^{n-1}{(-1)^{j-k}{n-1-kchoose {n-1-j}}prod limits_{i=1}^{m}{n-1-jchoose n-r_i -j}sum limits_{x=1}^{u_i}{x^{n-r_i}(u_i -x)^{r_i -1}}}$
$={n-1choose {n-1-k}}sum limits_{j=k}^{n-1}{(-1)^{j-k}{n-1-kchoose {n-1-j}}prod limits_{i=1}^{m}{n-1-jchoose n-r_i -j}sum limits_{l=n-r_i}^{n-1}{(-1)^{l-(n-r_i )}{r_i -1choose n-1-l}u_{i}^{n-1-l}sum limits_{x=1}^{u_i}x^{l}}}$
考虑原式的组合意义不难得到该容斥,最后那个式子直接拉格朗日插值出来就好了。
复杂度$O(mn^{3})$。上界为预处理插值,可以优化到$O(mn^{2})$,不过……
套路:
- 形如$Num(恰好k个)=Num(钦定k个)-Num(钦定k+1个)+cdots$的容斥$ ightarrow$每个大小为k+1的集合被计算$k+1choose k$次。(大部分时候k为0所以区别不开)
- 容斥时:注意区分对集合容斥和对集合大小容斥的区别。
代码:
#include<bits/stdc++.h> #define maxn 205 #define maxm 500005 #define inf 0x7fffffff #define mod 1000000007 #define ll long long #define rint register ll #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; struct node{ll x,y;}tp[maxn]; ll C[maxn][maxn],inv[maxn],R[maxn],U[maxn],L[maxn][maxn]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline ll pw(ll a,ll b){ll r=1;while(b)r=(b&1)?r*a%mod:r,a=a*a%mod,b>>=1;return r;} inline void init(ll n){ C[0][0]=1; for(ll i=1;i<=n;i++){ C[i][0]=1; for(ll j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; } } inline ll Lagrange(ll x,ll n){ for(ll i=1;i<=n+2;i++) tp[i].x=i,tp[i].y=(tp[i-1].y+pw(i,n))%mod; for(ll i=-n-2;i<=n+2;i++) inv[i+n+2]=pw((i+mod)%mod,mod-2); ll ans=0; for(ll i=1;i<=n+2;i++){ ll res=tp[i].y; for(ll j=1;j<=n+2;j++){ if(j==i) continue; res=res*(x-tp[j].x+mod)%mod*inv[tp[i].x-tp[j].x+n+2]%mod; } ans=(ans+res)%mod; } return ans; } int main(){ ll n=read(),m=read(),K=read(),mn=inf; for(ll i=1;i<=m;i++) U[i]=read(); for(ll i=1;i<=m;i++) R[i]=read(),mn=min(n-R[i],mn); init(n); ll ans=0; for(ll i=1;i<=m;i++) for(ll j=0;j<=n-1;j++){ if(j==0) L[i][j]=U[i]; else L[i][j]=Lagrange(U[i],j); } for(ll k=K,t1=1;k<=mn;k++,t1=t1*(mod-1)%mod){ ll r1=1; for(ll i=1;i<=m;i++){ ll t=n-R[i],r2=0; for(ll j=t,t2=1;j<=n-1;j++,t2=t2*(mod-1)%mod) r2=(r2+t2*C[n-1-t][n-1-j]%mod*pw(U[i],n-1-j)%mod*L[i][j]%mod)%mod; r1=r1*r2%mod*C[n-1-k][t-k]%mod; } ans=(ans+r1*t1%mod*C[n-1-K][n-1-k]%mod)%mod; } printf("%lld ",ans*C[n-1][K]%mod); return 0; }