BZOJ 4559: [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

分析

首先,运用一发容斥原理,求出所有人与B神每门课分数相对关系的不同方案数。

这个似乎大(wo)家(lan)都(de)会(hui)了(yi),我就不说了,详见代码里的f。

然后,我们就需要计算每门课每个人的分数的方案数。对于每一门课,我们分别计算,然后把它们乘起来。

方便起见,令总分为s,名次为rk。

设B神的分数为x,则方案数为x^(n-rk)*(s-x)^(rk-1)

展开得到c(rk-1,0)*s^(rk-1)*x^(n-rk)-c(rk-1,1)*s^(rk-2)*x^(n-rk+1)+c(rk-1,2)*s^(rk-3)*x^(n-rk+2)-……..

显然,我们需要对于x=1..s的所有情况求和。

我们把x次数相同的项放在一起,进行一波整理,问题就转化成了求1^k+2^k+…+s^k

我们设g(k)=1^k+2^k+…+s^k,我们列出一波式子然后观察:

(s+1)^k-s^k=c(k,1)*s^(k-1) +c(k,2)*s^(k-2) +…+c(k,k)*s^0

s^k-(s-1)^k=c(k,1)(s-1)^(k-1)+c(k,2)(s-1)^(k-2)+…+c(k,k)*(s-1)^0

………………………………………………………………………………………………

  2^k-1^k=c(k,1)*1^(k-1)     +c(k,2)*1^(k-2)     +...+c(k,k)*1^0

把这些式子全部相加,得到:

(s+1)^k-1=c(k,1)*g(k-1)+c(k,2)*g(k-2)+…+c(k,k)*g(0)

于是就可以通过递推的方式求出g

然后就做完了。

代码

#include <bits/stdc++.h>

typedef long long LL;

const int N = 105;
const int MOD = 1e9 + 7;

int n,m,k,jc[N],ny[N],po1[N],po2[N],f[N],u[N],r[N],g[N];

int ksm(int x,int y)
{
    int ans = 1;
    while (y)
    {
        if (y & 1) 
            ans = 1ll * ans * x % MOD;
        x = 1ll * x * x % MOD;
        y >>= 1;
    }
    return ans;
}

int C(int n,int m)
{
    if (n < m) 
        return 0;
    return 1ll * jc[n] * ny[m] % MOD * ny[n - m] % MOD;
}

int calc(int u,int r)
{
    for (int i = 0; i <= n + 1; i++) 
        po1[i] = ksm(i,r - 1), po2[i] = ksm(i,n - r);
    for (int i = 1; i <= n + 1; i++)
    {
        f[i] = 0;
        for (int j = 1; j <= i; j++)
            (f[i] += 1ll * po1[i - j] * po2[j] % MOD) %= MOD;
    }
    int ans = 0;
    for (int i = 1; i <= n + 1; i++)
    {
        int s1 = f[i], s2 = 1;
        for (int j = 1; j <= n + 1; j++)
            if (i != j) 
                s1 = 1ll * s1 * (u - j) % MOD, s2 = 1ll * s2 * (i - j) % MOD;
        (ans += 1ll * s1 * ksm(s2, MOD - 2) % MOD) %= MOD;
    }
    return ans;
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for (int i = 1; i <= m; i++) 
        scanf("%d",&u[i]);
    for (int i = 1; i <= m; i++) 
        scanf("%d",&r[i]);
    jc[0] = jc[1] = ny[0] = ny[1] = 1;
    for (int i = 2; i <= n; i++) 
        jc[i] = 1ll * jc[i - 1] * i % MOD, ny[i] = 1ll * (MOD - MOD / i) * ny[MOD % i] % MOD;
    for (int i = 2; i <= n; i++) 
        ny[i] = 1ll * ny[i] * ny[i - 1] % MOD;
    for (int i = 1; i <= m; i++) 
        g[i] = calc(u[i],r[i]);
    int ans = 0;
    for (int i = k; i < n; i++)
    {
        int w = 1ll * C(n - 1, i) * C(i, k) % MOD;
        for (int j = 1; j <= m; j++) 
            w = 1ll * w * C(n - i - 1, r[j] - 1) % MOD * g[j] % MOD;
        if ((i - k) & 1) 
            (ans -= w) %= MOD;
        else (ans += w) %= MOD;
    }
    printf("%d",(ans + MOD) % MOD);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值