• @hdu



    @description@

    定义矩阵 (A_i) 是一个大小为 (p^i*p^i) 的矩阵,其中 (p) 是第 (c) 个素数(c 给定),且 (A_i[x][y] = [C(x, y) mod p > 0])(其中 C(x, y) 是组合数)。
    行列从 0 开始计数。

    再定义 (F[i][j]) 表示 ((A_i)^j) 中所有元素之和。

    (sum_{i=1}^nsum_{j=1}^{k}F[i][j])。对 10^9 + 7 取模。

    Input
    第一行包含一个整数 T,然后接下来是 T 组数据:
    每一组数据包含三个整数 c, n, k (0 < n ≤ 10^9, 0 < c, k ≤ 10^5)。意义如上。

    Output
    对于每组数据,输出一个整数表示答案。

    Sample Input
    1
    1 1 1
    Sample Output
    3

    @solution@

    看上去这个题非常不可做,先定义了一个矩阵,然后又要求矩阵幂,然后又要把这个矩阵幂中所有元素求和,然后又要把这些矩阵求和的结果再求和。
    但是你只需要找到突破口,剩下的部分就一气呵成(其实一气呵成这个词不能这么用。。。今年中考考了这玩意儿,然后做错了,所以印象深刻。。。)
    怎么找突破口?你只需要把题目倒过来读注意到矩阵的定义涉及到组合数对素数取模,于是就可以牵扯出 lucas 定理。

    lucas 定理是什么?其实很简单。对于 (C(n, m) mod p),我们将 n, m 拆成 p 进制数的形式,即 (n = n_0 + n_1*p^1 + ..., m = m_0 + m_1*p^1 + ...)
    于是 lucas 定理告诉我们:(C(n, m) = C(n_0, m_0)*C(n_1, m_1)*... mod p)
    证明这个定理也不难,只是与这道题无关所以暂且不提。

    很显然 (C(n, m) mod p ge 0),所以我们只需要判断 (C(n, m) mod p = 0) 是否成立即可。
    又因 (n_i < p, m_i < p) (因为是 p 进制嘛),所以 (C(n_i, m_i)) 不可能含因子 p,故我们只需要对于每一个 i 判断是否 (C(n_i, m_i) = 0),而前面那个等价于 (n_i > m_i)
    所以 (A_i[x][y]) 为 1 等价于在 p 进制下 x 的每一位都 ≤ y 的对应位。

    现在考虑 ((A_i)^j[x][y]) 怎么求。
    先试着考虑 ((A_i)^2[x][y]),可以发现 ((A_i)^2[x][y] = sum_{z}A_i[x][z]*A_i[z][y]),只有当 (A_i[x][z], A_i[z][y]) 都为 1 时才会产生贡献。
    这有点儿像偏序的关系,因为有些传递性和偏序形成链的感觉在里面。
    或者用图论的语言,如果 (A_i[x][y] = 1) 则 x 向 y 连边。则 ((A_i)^2[x][y]) 则有点儿像走两步(中途可以停留在原地)从 x 到达 y 的方案数。
    从而简单推广,可以得到 ((A_i)^j[x][y]) 表示走 j 步从 x 到达 y 的方案数。

    那么 F[i][j] 的含义是什么?为了计数的方便我们暂且不用图论的语言描述。
    F[i][j] 表示长度为 i 的 p 进制的数字串,选出 j+1 个记为 s0, s1, ... sj,对于第 x 位(1≤x≤n)始终满足 s0[x] ≤ s1[x] ≤ ... ≤ sj[x] 的方案总数。
    怎么求 F[i][j] 呢?其实也比较简单。因为每一位都是独立的,所以考虑某一位然后乘法原理乘起来即可。
    我们发现如果确定了 s0[x], s1[x], ..., sj[x] 分别是哪些数,它们的顺序始终是一定的(即排序过后的顺序)。所以我们相当于是求 x1 + ... + xp = j + 1 的非负整数解的个数。经典的组合数学问题,答案为 C(j+p, j+1)。
    于是 F[i][j] = C(j+p, j+1)^i。

    于是 (sum_{i=1}^nsum_{j=1}^{k}F[i][j] = sum_{i=1}^nsum_{j=1}^{k}C(j+p, j+1)^i = sum_{j=1}^{k}sum_{i=1}^nC(j+p, j+1)^i)
    枚举 j 然后等比数列求和即可。
    注意 C(j+p, j+1) 与 C(j+p+1, j+1+1) 之间实际上是有倍数的关系(你可以把它们拆成阶乘形式以观察到这一点)。于是我们可以直接递推而不用预处理阶乘。

    @accepted code@

    #include<cstdio>
    const int MAXM = 1299709;
    const int MOD = int(1E9) + 7;
    int pow_mod(int b, int p) {
        int ret = 1;
        while( p ) {
            if( p & 1 ) ret = 1LL*ret*b%MOD;
            b = 1LL*b*b%MOD;
            p >>= 1;
        }
        return ret;
    }
    int prm[MAXM + 5], pcnt;
    bool nprm[MAXM + 5];
    void init() {
        for(int i=2;i<=MAXM;i++) {
            if( !nprm[i] )
                prm[++pcnt] = i;
            for(int j=1;1LL*i*prm[j]<=MAXM;j++) {
                nprm[i*prm[j]] = true;
                if( i % prm[j] == 0 )
                    break;
            }
        }
    }
    int solve(int p, int n, int k) {
        int ans = 0, tmp = p;
        for(int j=1;j<=k;j++) {
            tmp = 1LL*tmp*(j + p)%MOD*pow_mod(j + 1, MOD - 2)%MOD;
            if( tmp == 1 )
                ans = (ans + n)%MOD;
            else ans = (ans + 1LL*(pow_mod(tmp, n + 1) - 1)*pow_mod(tmp - 1, MOD-2)%MOD - 1)%MOD;
        }
        return (ans + MOD)%MOD;
    }
    int main() {
        init();
        int T; scanf("%d", &T);
        for(int i=1;i<=T;i++) {
            int c, n, k; scanf("%d%d%d", &c, &n, &k);
            printf("%d
    ", solve(prm[c], n, k));
        }
    }
    

    @details@

    似乎总喜欢废话很多。。。明明一个不是很复杂的题目却写了这么多东西。。。

    这道题有一个点就是:等比数列要特判公比为 1 的情况。
    。。。虽然数学上经常考这个东西,不过还是没记住。。。

  • 相关阅读:
    Win下循环进入目录启动执行某任务
    Mysql数据库搭建-Windows
    Linux的服务器初始优化脚本。
    Linux下Find命令的使用
    一些判断Linux是否被黑的经验
    搭建docker私有仓库
    进程退出:SIGINT、SIGTERM和SIGKILL区别
    dockerfile使用
    k8s-ingress安装
    k8s-service
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11147167.html
Copyright © 2020-2023  润新知