bzoj P4870: [Shoi2017]组合数问题——solution

题意:求解——

$$(C^{r}_{nk}+C^{r+k}_{nk}+C^{r+2k}_{nk}+...+C^{r+(n-1)k}_{nk}+...)mod(P)$$

其中$C^{m}_{n}$表示从n中选m个的方案数

保证$1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^{30} − 1$

http://www.lydsy.com/JudgeOnline/problem.php?id=4870

 

一看r,k很小就很自然地想到矩阵快速幂;

然后枚举nk

一开始打算横着递推,处理出每个C,同时处理C的部分和,然而横着递推有问题;

最后发现竖着递推,直接处理C的部分和非常方便。

S(x,y)表示C(ik+x,y)的和,可以从S((x-1+k)%k,y-1)+S(x,y-1)得出;

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
LL P,N,K,R;
struct Matrix{
    LL _[55][55];
};
Matrix operator * (Matrix a,Matrix b){
    Matrix c;
    int i,j,k;
    memset(c._,0,sizeof(c._));
    for(i=0;i<55;i++)
        for(k=0;k<55;k++)
            if(a._[i][k])
                for(j=0;j<55;j++)
                    if(b._[k][j])
                        (c._[i][j]+=(a._[i][k]*b._[k][j])%P)%=P;
    return c;
}
Matrix x,ret;
void Sqr(LL );
int main()
{
    int i;
    scanf("%lld%lld%lld%lld",&N,&P,&K,&R);
    for(i=0;i<K;i++)
        x._[(i-1+K)%K][i]++,x._[i][i]++;
    Sqr(N*K);
    printf("%lld\n",ret._[0][R]);
    return 0;
}
void Sqr(LL n){
    int i;
    for(i=0;i<55;i++)ret._[i][i]=1;
    while(n){
        if(n&1)
            ret=ret*x;
        n>>=1,x=x*x;
    }
}

存在的问题:
见到组合数就认为不可能在合理的时间内完成行间递推

posted @ 2018-03-08 09:23  F.W.Nietzsche  阅读(131)  评论(0编辑  收藏  举报