loj #2143. 「SHOI2017」组合数问题

#2143. 「SHOI2017」组合数问题

 

题目描述

组合数 Cnmmathrm{C}_n^mCnm​​ 表示的是从 nnn 个互不相同的物品中选出 mmm 个物品的方案数。举个例子, 从 (1,2,3)(1, 2, 3)(1,2,3) 三个物品中选择两个物品可以有 (1,2)(1, 2)(1,2),(1,3)(1, 3)(1,3),(2,3)(2, 3)(2,3) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 Cnmmathrm{C}_n^mCnm​​ 的一般公式:

Cmn=n!m! (nm)!

其中 n!=1×2×⋯×nn! = 1 imes 2 imes cdots imes nn!=1×2××n。(特别地,当 n=0n = 0n=0 时,n!=1n! = 1n!=1;当 m>nm > nm>n 时,Cnm=0mathrm{C}_n^m = 0Cnm​​=0。)

小葱在 NOIP 的时候学习了 Cijmathrm{C}_i^jCij​​ 和 kkk 的倍数关系,现在他想更进一步,研究更多关于组合数的性质。小葱发现,Cijmathrm{C}_i^jCij​​ 是否是 kkk 的倍数,取决于 Cjimodk 是否等于 000,这个神奇的性质引发了小葱对 modmathrm{mod}mod 运算(取余数运算)的兴趣。现在小葱选择了是四个整数 n,p,k,rn, p, k, rn,p,k,r,他希望知道

(i=0Cik+rnk)modp,

(Crnk+Ck+rnk+C2k+rnk++C(n1)k+rnk+Cnk+rnk+)modp

的值。

输入格式

第一行有四个整数 n,p,k,rn, p, k, rn,p,k,r,所有整数含义见问题描述。

输出格式

一行一个整数代表答案。

样例

样例输入 1

2 10007 2 0

样例输出 1

8

样例解释 1

C40+C42+C44+⋯=1+6+1=8mathrm{C}_4^0 + mathrm{C}_4^2 + mathrm{C}_4^4 + cdots = 1 + 6 + 1 = 8C40​​+C42​​+C44​​+=1+6+1=8。

样例输入 2

20 10007 20 0

样例输出 2

176

数据范围与提示

对于 30%30\%30% 的测试点,1≤n,k≤301 leq n, k leq 301n,k30,ppp 是质数;
对于另外 5%5\%5% 的测试点,p=2p = 2p=2;
对于另外 5%5\%5% 的测试点,k=1k = 1k=1;
对于另外 10%10\%10% 的测试点,k=2k = 2k=2;
对于另外 15%15\%15% 的测试点,1≤n≤103,1≤k≤501 leq n leq 10^3, 1 leq k leq 501n103​​,1k50,ppp 是质数;
对于另外 15%15\%15% 的测试点,1≤n×k≤1061 leq n imes k leq 10^61n×k106​​,ppp 是质数;
对于另外 10%10\%10% 的测试点,1≤n≤109,1≤k≤501 leq n leq 10^9, 1 leq k leq 501n109​​,1k50,ppp 是质数;
对于 100%100\%100% 的测试点,1≤n≤109,0≤r<k≤50,2≤p≤230−11 leq n leq 10^9, 0 leq r < k leq 50, 2 leq p leq 2^{30} - 11n109​​,0r<k50,2p230​​1。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int f[1000010][51],n,p,k,r;
int main(){
    scanf("%d%d%d%d",&n,&p,&k,&r);
    f[0][0]=1;
    for(int i=1;i<=n*k;i++)
        for(int j=0;j<k;j++){
            if(j!=0)f[i][j]=(f[i-1][j]+f[i-1][j-1])%p;
            else f[i][j]=(f[i-1][j]+f[i-1][j+k-1])%p;
        }
    printf("%d",f[n*k][r]);
}
60分 暴力
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int P,K,R;
long long N;
struct matrix{
    int n,m,a[51][51];
    matrix(){memset(a,0,sizeof(a));}
    matrix operator * (const matrix &c)const{
        matrix res;res.n=n;res.m=c.m;
        for(int i=0;i<n;i++)
            for(int j=0;j<c.m;j++)
                for(int k=0;k<m;k++)
                    res.a[i][j]=(res.a[i][j]+1LL*a[i][k]*c.a[k][j]%P)%P;
        return res;
    }
}a,b;
void Pow(matrix x,long long y){
    while(y){
        if(y&1)
            a=a*x;
        x=x*x;
        y>>=1;
    }
}
int main(){
    cin>>N;
    scanf("%d%d%d",&P,&K,&R);
    N=1LL*N*K;
    a.n=1;a.m=K;a.a[0][0]=1;
    b.n=b.m=K;
    for(int i=0;i<K;i++)b.a[i][i]++,b.a[i][(i+1)%K]++;
    Pow(b,N);
    printf("%d",a.a[0][R]);
    return 0;
}
100分 矩阵快速幂优化dp
原文地址:https://www.cnblogs.com/thmyl/p/8908173.html