[JLOI2016] 成绩比较

题意:

有n个人,m门课,每个人在每门课的得分是一个$[1,u_i ]$之间的整数。

你知道自己在每门课的排名$r_i$,即有$r_i-1$个人得分高于你,$n-r_i$个人得分不高于你(不含自己)。

求你恰好碾压k个人的方案数,a碾压b的含义为a每门课的得分都不低于b的得分。

$n,m\leq 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}{m\choose x}$的本质区别:前者是分两个集合,后者是分三个集合。

也就是说对于每个大小为x的集合S,前者只被算了一遍,后者被算了${n-x}\choose{m-x}$遍。

那么我们考虑这道题容斥的底层过程:有两个大小为k的集合S1和S2,它们的并集是大小为k+1的S3。

那S3是被算了一遍还是被算了${{k+1}\choose{k}}$遍呢?显然是后者,于是一开始就应该用后者计算。

(upd:另一种解释:是对于每个集合容斥而不是对于集合大小容斥,所以每次必须钦定k个不动。)

回到容斥,答案就是

${n-1\choose {n-1-k}}\sum \limits_{j=k}^{n-1}{(-1)^{j-k}{n-1-k\choose {n-1-j}}\prod \limits_{i=1}^{m}{n-1-j\choose n-r_i -j}\sum \limits_{x=1}^{u_i}{x^{n-r_i}(u_i -x)^{r_i -1}}}$

最后面那个式子可以用二项式定理拆一下,我是在容斥里面写了个子容斥。(所以说我nt呢)

大概形如

${n-1\choose {n-1-k}}\sum \limits_{j=k}^{n-1}{(-1)^{j-k}{n-1-k\choose {n-1-j}}\prod \limits_{i=1}^{m}{n-1-j\choose n-r_i -j}\sum \limits_{x=1}^{u_i}{x^{n-r_i}(u_i -x)^{r_i -1}}}$

$={n-1\choose {n-1-k}}\sum \limits_{j=k}^{n-1}{(-1)^{j-k}{n-1-k\choose {n-1-j}}\prod \limits_{i=1}^{m}{n-1-j\choose n-r_i -j}\sum \limits_{l=n-r_i}^{n-1}{(-1)^{l-(n-r_i )}{r_i -1\choose 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$的容斥$\rightarrow$每个大小为k+1的集合被计算$k+1\choose 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\n",ans*C[n-1][K]%mod);
    return 0;
}
成绩比较

 

posted @ 2020-07-12 22:55  Fugtemypt  阅读(165)  评论(0编辑  收藏  举报