BZOJ 4559 [JLoi2016]成绩比较

来源:互联网 发布:淘宝店铺和旺铺的区别 编辑:程序博客网 时间:2024/05/16 16:56

DP+组合计数

不难想到记f[i][j]表示统计到第i门课,此时j个人被碾压的方案数。
f[i][j] = f[i-1][k] * C[k][j] * C[n-k-1][n-rank[i]-j] * P[i] (k >= j)
意义就是从前面的k个人里面选出j个人继续碾压,再选出一些人来排在自己的排名后面,再乘上分配给n个人1~U[i]的分数的合法方案数P[i]。

我们发现 P[i]=U[i]j=1jnrank[i](U[i]j)rank[i]1
二项式展开,再把外面的j乘进去,就变成一个求类似ai=1ib的问题,这东西显然可以强行插值。然而有一个更 妙的做法,不想插值的同学请点这里。

#include<cstdio>#include<cstring>#define N 105#define MOD 1000000007using namespace std;namespace runzhe2000{    typedef long long ll;    int n, m, d, u[N], r[N], g[N], C[N][N], f[N][N];    int fpow(int a, int b)    {        int r = 1;        for(; b; b>>=1)        {            if(b&1) r = (ll)r*a%MOD;            a = (ll)a*a%MOD;        }        return r;    }    void main()    {        scanf("%d%d%d",&n,&m,&d);        for(int i = 1; i <= m; i++) scanf("%d",&u[i]);        for(int i = 1; i <= m; i++) scanf("%d",&r[i]);        C[0][0] = 1; for(int i = 1; i <= n+1; i++) {C[i][0] = 1; for(int j = 1; j <= i; j++) C[i][j] = (C[i-1][j-1] + C[i-1][j]) % MOD; }        f[0][n-1] = 1;        for(int i = 1; i <= m; i++)        {            g[0] = u[i];            for(int j = 1; j <= n; j++)            {                g[j] = (fpow(u[i]+1, j+1) - 1 + MOD) % MOD;                for(int k = 0; k < j; k++)                    (g[j] -= (ll)C[j+1][k] * g[k] % MOD ) %= MOD;                g[j] = (ll)g[j] * fpow(j+1, MOD-2) % MOD;            }            int P = 0, inv = fpow(u[i], MOD-2), v = fpow(u[i], r[i]-1), q = 1;            for(int j = 0; j <= r[i]-1; j++)            {                (P += (ll)q * C[r[i]-1][j] % MOD * v % MOD * g[n-r[i]+j] % MOD) %= MOD;                v = (ll) v * inv % MOD;                q = -q;            }            for(int j = d; j <= n; j++)                for(int k = j; k <= n; k++)                    if(n-r[i]-j >= 0)(f[i][j] += (ll)f[i-1][k] * C[k][j] % MOD * C[n-k-1][n-r[i]-j] %MOD * P % MOD) %= MOD;        }        printf("%d\n",(f[m][d]+MOD)%MOD);    }}int main(){    runzhe2000::main();}
0 0
原创粉丝点击