zoukankan      html  css  js  c++  java
  • [JLOI 2016]成绩比较

    Description

    题库链接( ext{bzoj}) 不知道为什么过不了啊... ( ext{luogu loj}) 都能过...就给 ( ext{luogu}) 的链接了...)

    共有 (n) 位同学, (M) 门必修课。一位同学在必修课上可以获得的分数是 (1)(U_i) 中的一个整数。

    如果在每门课上 (A) 获得的成绩均小于等于 (B) 获得的成绩,则称 (A)(B) 碾压。在 (B) 神的说法中,共有 (K) 位同学被他碾压(不包括他自己)。

    (B) 神在第 (i) 门必修课中排名为 (R_i) 。这里的排名是指:如果 (B) 神某门课的排名为 (R) ,则表示有且仅有 (R-1) 位同学这门课的分数大于 (B) 神的分数,有且仅有 (N-R) 位同学这门课的分数小于等于 (B) 神(不包括他自己)。

    我们需要求出全系所有同学每门必修课得分的情况数,使其既能满足B神的说法,也能符合排名。这里两种情况不同当且仅当有任意一位同学在任意一门课上获得的分数不同。

    计算出情况数模 (10^9+7) 的。

    (Nleq 100,Mleq 100,Uileq 10^9)

    Solution

    由于只有 (k) 名同学被碾压,故要保证其余的 (n-1-k) 名同学至少有一门分数要高于他。

    我们有 (n-1choose k) 种方案选出被碾压的 (k) 个人,对于剩下的 (n-1-k) 个人,我们记 (f_x) 为至多有 (x) 个人满足至少一门分数高于他,那么 (f_x=prodlimits_{i=1}^m{xchoose R_i-1}) 。再设 (g_x) 为恰有 (x) 个人满足至少一门分数高于他,显然我们要求的是 (g_{n-k-1})

    容易得到

    [f_x=sum_{i=1}^x {xchoose i}g_i]

    由二项式反演,得到

    [g_x=sum_{i=1}^x (-1)^{x-i}{xchoose i}f_i]

    那么

    [g_{n-k-1}=sum_{i=1}^{n-k-1}(-1)^{n-k-1-i}{n-k-1choose i}prod_{j=1}^m{ichoose R_j-1}]

    不过这只考虑了合法的安排相对分数高低情况,还没有考虑具体分数的关系。

    不妨记 (S) 为一种人员安排情况下不同的分数安排方法。

    我们枚举他的分数得到

    [egin{aligned}S&=prod_{i=1}^msum_{j=1}^{U_i}j^{n-R_i}(U_i-j)^{R_i-1}\&=prod_{i=1}^msum_{j=1}^{U_i}j^{n-R_i}sum_{k=0}^{R_i-1}(-1)^k{R_i-1choose k}j^kU_i^{R_i-1-k}\&=prod_{i=1}^msum_{j=1}^{U_i}sum_{k=0}^{R_i-1}j^{n-R_i+k}(-1)^k{R_i-1choose k}U_i^{R_i-1-k}\&=prod_{i=1}^msum_{k=0}^{R_i-1}(-1)^k{R_i-1choose k}U_i^{R_i-1-k}sum_{j=1}^{U_i}j^{n-R_i+k}end{aligned}]

    注意到 (Q=sumlimits_{j=1}^{U_i}j^{n-R_i+k}) 中的 (U_i) 会很大,显然不能直接枚举,我们可以插值解决。预处理出 (S) 的复杂度是 (O(mn^2)) 的。

    最终答案就是

    [{n-1choose k}g_{n-k-1}S]

    Code

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 233+5, yzh = 1e9+7;
    
    int n, m, k, U[N], R[N];
    int fac[N], ifac[N];
    int prime[N], tot, isprime[N], f[N], s1[N], s2[N];
    
    int quick_pow(int a, int b) {
        int ans = 1;
        while (b) {
            if (b&1) ans = 1ll*ans*a%yzh;
            b >>= 1, a = 1ll*a*a%yzh;
        }
        return ans;
    }
    int C(int n, int m) {return 1ll*fac[n]*ifac[m]%yzh*ifac[n-m]%yzh; }
    int lagrange(int *y, int k, int xi) {
        int ans = 0; ++k;
        s1[0] = xi, s2[k+1] = 1;
        for (int i = 1; i <= k; i++) s1[i] = 1ll*s1[i-1]*(xi-i)%yzh;
        for (int i = k; i >= 0; i--) s2[i] = 1ll*s2[i+1]*(xi-i)%yzh;
        for (int i = 0; i <= k; i++)
            (ans += 1ll*y[i]*(i == 0 ? 1 : s1[i-1])%yzh*s2[i+1]%yzh*ifac[i]%yzh*((k-i)&1 ? -1 : 1)*ifac[k-i]%yzh) %= yzh;
        return ans;
    }
    int getQ(int xi, int k) {
        tot = 0; memset(isprime, 1, sizeof(isprime));
        isprime[1] = 0; f[0] = 0, f[1] = 1;
        for (int i = 2; i <= k+1; i++) {
            if (isprime[i]) prime[++tot] = i, f[i] = quick_pow(i, k);
            for (int j = 1; j <= tot && i*prime[j] <= k+1; j++) {
                isprime[i*prime[j]] = 0, f[i*prime[j]] = 1ll*f[i]*f[prime[j]]%yzh;
                if (i%prime[j] == 0) break;
            }
        }
        for (int i = 1; i <= k+1; i++) (f[i] += f[i-1]) %= yzh;
        if (xi <= k+1) return f[xi];
        return lagrange(f, k, xi);
    }
    int getS() {
        int ans = 1;
        for (int i = 1; i <= m; i++) {
            int sum = 0;
            for (int k = 0; k <= R[i]-1; k++)
                if (k&1) (sum -= 1ll*C(R[i]-1, k)*quick_pow(U[i], R[i]-1-k)%yzh*getQ(U[i], n-R[i]+k)%yzh) %= yzh;
                else (sum += 1ll*C(R[i]-1, k)*quick_pow(U[i], R[i]-1-k)%yzh*getQ(U[i], n-R[i]+k)%yzh) %= yzh;
            ans = 1ll*ans*sum%yzh;
        }
        return ans;
    }
    void work() {
        scanf("%d%d%d", &n, &m, &k);
        fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
        for (int i = 2; i < N; i++) ifac[i] = -1ll*yzh/i*ifac[yzh%i]%yzh;
        for (int i = 2; i < N; i++)
            ifac[i] = 1ll*ifac[i-1]*ifac[i]%yzh,
            fac[i] = 1ll*fac[i-1]*i%yzh;
        for (int i = 1; i <= m; i++) scanf("%d", &U[i]);
        for (int i = 1; i <= m; i++) scanf("%d", &R[i]);
        int S = getS(), ans = 0;
        for (int i = 1; i <= n-k-1; i++) {
            int sum = 1;
            for (int j = 1; j <= m; j++) sum = 1ll*sum*C(i, R[j]-1)%yzh;
            if ((n-k-1-i)&1) (ans -= 1ll*sum*C(n-k-1, i)%yzh) %= yzh;
            else (ans += 1ll*sum*C(n-k-1, i)%yzh) %= yzh;
        }
        ans = 1ll*ans*S%yzh*C(n-1, k)%yzh;
        printf("%d
    ", (ans+yzh)%yzh);
    }
    int main() {work(); return 0; }
  • 相关阅读:
    【.net】未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序解决办法
    【JS】JavaScript中Null和undefind区别
    【SQL】SQL中on条件与where条件的区别
    【C#】C#创建自定义Object对象
    【.NET】asp.net Redirect 图片路径
    【JQ】jq动态绑定事件.on()、解绑事件off()
    【.NET】using 语句中使用的类型必须可隐式转换为"System.IDisposable"
    C# enum、int、string三种类型互相转换
    js中Date与timestamp(时间戳)的相互转换
    2. 自动化运维系列之Cobbler给Openstack节点安装操作系统。
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/9283669.html
Copyright © 2011-2022 走看看