[codeforces582D]Number of Binominal Coefficients

19 篇文章 0 订阅
8 篇文章 0 订阅

题目大意

给定质数p和正整数α、k,求多少对(n,k)满足0≤m≤n≤α 且 Cmn pk 的倍数

p,k≤ 109
n≤ 101000

分析

此题要用到库默尔定理:设m,n为正整数,p为素数,则 Cnm+n 含p的幂次等于p进制下m与n相加的进位次数。
有了这个定理就可以很好地解题了。先把n转化为p进制,然后考虑数位DP。设f[i][j][x][y],表示从高到低确定了p进制下n,m的前i位,进了j次位,x和y是01状态,分别表示前i位之和是否与n在p进制下的前i位完全相同、第i+1位是否进了位到第i位。然后转移需要分类讨论。

由于n在10进制下只有1000位,所以当p=2时只有3000多位。足以通过此题。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=3405,mo=1e9+7;

typedef long long LL;

int a[N],b[N],c[N],f[N][N][2][2],p,k,cnt,n,m,ans;

char s[N];

int main()
{
    scanf("%d%d%s",&p,&k,s);
    n=strlen(s);
    for (int i=0;i<n;i++) a[n-i]=s[i]-'0';
    for (m=0;!(n==1 && a[1]==0);)
    {
        int res=0;
        memset(c,0,sizeof(c));
        for (int i=n;i;i--)
        {
            c[i]=((LL)res*10+a[i])/p;
            res=((LL)res*10+a[i])%p;
        }
        b[m++]=res;
        memcpy(a,c,sizeof(a));
        for (;n>1 && !a[n];n--);
    }
    f[m][0][1][0]=1;
    for (int i=m-1;i>=0;i--)
    {
        for (int j=0;j<m-i;j++)
        {
            int t00=(LL)p*(p+1)/2%mo,t01=(LL)(p-1)*p/2%mo,t10=((LL)p*p-t00)%mo,t11=((LL)p*p-t01)%mo;
            f[i][j][0][0]=(f[i][j][0][0]+(LL)f[i+1][j][0][0]*t00+(LL)f[i+1][j][0][1]*t10)%mo;
            f[i][j+1][0][1]=(f[i][j+1][0][1]+(LL)f[i+1][j][0][0]*t01+(LL)f[i+1][j][0][1]*t11)%mo;
            if (!b[i])
            {
                f[i][j][1][0]=(f[i][j][1][0]+f[i+1][j][1][0]+(LL)f[i+1][j][1][1]*(p-1))%mo;
                f[i][j+1][1][1]=(f[i][j+1][1][1]+(LL)f[i+1][j][1][1]*p)%mo;
                continue;
            }
            t00=(LL)b[i]*(b[i]+1)/2%mo; t01=(LL)(b[i]-1)*b[i]/2%mo;
            t10=(LL)(p+p-b[i]-1)*b[i]/2%mo; t11=(LL)(p+p-b[i]+1)*b[i]/2%mo;
            f[i][j][0][0]=(f[i][j][0][0]+(LL)f[i+1][j][1][0]*t00+(LL)f[i+1][j][1][1]*t10)%mo;
            f[i][j+1][0][1]=(f[i][j+1][0][1]+(LL)f[i+1][j][1][0]*t01+(LL)f[i+1][j][1][1]*t11)%mo;
            f[i][j][1][0]=(f[i][j][1][0]+(LL)f[i+1][j][1][0]*(b[i]+1)+(LL)f[i+1][j][1][1]*(p-b[i]-1))%mo;
            f[i][j+1][1][1]=(f[i][j+1][1][1]+(LL)f[i+1][j][1][0]*b[i]+(LL)f[i+1][j][1][1]*(p-b[i]))%mo;
        }
    }
    ans=0;
    for (int i=k;i<=m;i++) ans=(ans+f[0][i][0][0]+(LL)f[0][i][1][0])%mo;
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值