• 【题解】[六省联考 2017] 组合数问题


    题意

    给定 $n,k,p,r$。

    $$\sum_{i=0}^{+∞}\binom{nk}{r+ki} \bmod p$$

    范围:$1 \le n \le 10^9$,$0 \le r < k \le 50$,$2 \le p < 2^{30}$。

    题解

    写出组合数的递推式:

    $$\binom{n}{m}=\binom{n-1}{m}+\binom{n-1}{m-1}$$

    令 $f_{i,j}=\binom{n}{m}$,那么有 $f_{i,j}=f_{i-1,j}+f_{i-1,j-1}$。

    直接求时间空间都炸,考虑优化递推式。

    由于我们要求出所有 $a \equiv r \pmod k$ 的 $\binom{nk}{a}$ 之和,所以第二维只需在意对 $k$ 取模的值。

    把第二维压缩,重新定义 $f_{i,j}$ 为满足 $a \equiv r \pmod k$ 的 $\binom{i}{a}$ 之和。

    那么有 $f_{0,0}=1$,$f_{i,j}=f_{i-1,j}+f_{i-1,(j-1) \bmod k}$ 。

    此时式子出现了 $\bmod k$,发现存在 $k=1$ 的情况,此时 $f_{i,0}$ 在推导下等于 $1$,显然不对,所以要特判算一下,算得结果为 $\sum\limits_{i=0}^n \binom{n}{i}=2^n$。

    对于 $k \neq 1$ 的情况,发现 $f$ 中第一维 $i$ 只与第 $i-1$ 的状态有关,所以上矩阵快速幂(向量乘矩阵)即可。

    初始状态构造方式:向量 $A$ 的第 $0$ 项为 $1$,矩阵 $E$ 中 $\forall i$,$\ E_{i,i},E_{(i-1) \bmod k,i}$ 设为 $1$。

    这么做时间复杂度 $\mathcal{O}(k^3 \log nk)$。当然有更快的 $\mathcal{O}(k^2 (\log n+\log k))$ 卷积做法,这里先不说了。

    代码

    #include <bits/stdc++.h>
    #define eps 1e-6
    typedef long long ll;
    using namespace std;
    const int INF = 1e9;
    
    struct Matrix{
        int m[55][55], h, w;
    }A, E;
    int n, p, k, r;
    ll nn;
    
    int qpow(int a, int b){
        int t = 1;
        while(b){
            if(b & 1) t = 1ll * t * a % p;
            a = 1ll * a * a % p;
            b >>= 1;
        }
        return t;
    }
    
    Matrix operator * (Matrix x, Matrix y){
        Matrix z;
        memset(z.m, 0, sizeof(z.m));
        z.h = x.h, z.w = y.w;
        for(int k = 0; k < x.w; k++)
            for(int i = 0; i < x.h; i++)
                for(int j = 0; j < y.w; j++)
                    z.m[i][j] = (z.m[i][j] + 1ll * x.m[i][k] * y.m[k][j] % p) % p;
        return z;
    }
    
    int main(){
    
        scanf("%d%d%d%d", &n, &p, &k, &r), nn = 1ll * n * k;
        if(k == 1){
            printf("%d\n", qpow(2, n));
            return 0;
        }
        A.h = 1, A.w = E.h = E.w = k, A.m[0][0] = 1;
        for(int i = 0; i < k; i++) E.m[i][i] = 1;
        for(int i = 0; i < k; i++) E.m[(i - 1 + k) % k][i] = 1;
        while(nn){
            if(nn & 1) A = A * E;
            E = E * E;
            nn >>= 1;
        }
        printf("%d\n", A.m[0][r]);
        
        return 0;
    }
  • 相关阅读:
    jvm的那些设置参数你都知道吗
    进程之间究竟有哪些通信方式
    从零开始带你成为JVM实战高手
    java 面试题目(java高级架构)
    面试要点补充
    Paxos算法与Zookeeper分析,zab (zk)raft协议(etcd) 8. 与Galera及MySQL Group replication的比较
    一文搞懂Raft算法
    Python 循环语句
    Python 条件语句
    Python 运算符
  • 原文地址:https://www.cnblogs.com/zengpeichen/p/15731035.html
Copyright © 2020-2023  润新知