【20200205】【yx】CF582D Number of Binominal Coefficients DP

https://codeforces.com/problemset/problem/582/D

题意

给定p,a(1~1e9),求有多少对(n,k)满足 C n k C_{n}^{k} Cnk可被 p a p^a pa整除(1<=k<=n<=A, A= 1 0 1000 10^{1000} 101000

解题

分析下,n!所含的p的次数相当于是n在p进制下按位加权的数位和,
这样 C n k = n ! k ! ( n − k ) ! C_{n}^{k}=\frac{n!}{k!(n-k)!} Cnk=k!(nk)!n!所含的p的次数为k+(n-k)在p进制下的进位次数(可能要想一下,或可直接用一个叫kummer定理的东西,意思一样)
然后把dp出来就好

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e4 + 100;
const int mo = 1e9 + 7;
int aa[3500], cnt, bb[3500], tot, t[3500], cnt2;
int f[3500][3500][2][2];


const int inv2 = 5e8 + 4;

inline int add(ll a, ll b) {
	return a+b>mo?a+b-mo:a+b;
}

inline int mns(int a, int b) { 
return a-b<0?a-b+mo:a-b;
}

void cov(int p) {
    ll val = 0; int flg = 0;
    while (1) {
        flg = 0;
        for (int i = 0; i < cnt; ++i) {
            val = (1ll*val * 10 +aa[i]);
            if (val < (1ll)*p)t[tot++] = 0;
            else {
                flg = 1;
                t[tot++] = val / p;
                val %= p;
            }
        }
        for (int i = 0; i < cnt; ++i)aa[i] = t[i];
        tot = 0;
        bb[cnt2++] = val, val = 0;
        if (val < p && !flg)break;
    }
}


signed main() {
    int p, a;
    scanf("%d%d", &p, &a);
    aa[cnt] = getchar();
    char c = '9';
    while (c = getchar()) {
        if (c > '9' || c < '0')break;
        aa[cnt++] = c - '0';
    }
    cov(p);
    if(cnt2<a){
        printf("0\n");
    }
    else{
        f[cnt2+1][0][0][1] = 1;
        int p1= 1ll*(p + 1) * p % mo * inv2 % mo,p2= 1ll*(p - 1) * p % mo * inv2 % mo;
        for (int i = cnt2; i; --i) {
            int x1 = 1ll*(bb[i-1]+1)*bb[i-1]%mo*inv2%mo,x2= 1ll*(bb[i - 1] - 1) * bb[i - 1] % mo * inv2 % mo, px= 1ll*p * bb[i - 1] % mo;
            for (int j = 0; j <=cnt2 - i; ++j) {

                int tmp = f[i+1][j][0][0];
                f[i][j][0][0]=add(f[i][j][0][0], 1ll*tmp * p1 % mo);
                f[i][j+1][1][0]=add(f[i][j+1][1][0], 1ll*tmp * p2 % mo);

                tmp = f[i+1][j][1][0];
                f[i][j][0][0]=add(f[i][j][0][0],1ll* tmp * p2 % mo);
                f[i][j+1][1][0]=add(f[i][j+1][1][0], 1ll*tmp * p1 % mo);

                tmp = f[i+1][j][0][1];
                f[i][j][0][0]=add(f[i][j][0][0], 1ll*tmp*x1%mo);
                f[i][j][0][1]=add(f[i][j][0][1], 1ll*tmp*(bb[i-1]+1)%mo);
                f[i][j+1][1][0]=add(f[i][j+1][1][0], 1ll*tmp * x2 % mo);
                f[i][j+1][1][1]=add(f[i][j+1][1][1], 1ll*tmp*bb[i-1]%mo);

                tmp = f[i+1][j][1][1];
                f[i][j][0][0]=add(f[i][j][0][0], 1ll*tmp*mns(px, x1)%mo);
                f[i][j][0][1]=add(f[i][j][0][1], 1ll*tmp*(p -bb[i-1] - 1)%mo);
                f[i][j+1][1][0]=add(f[i][j+1][1][0], 1ll*tmp * mns(px, x2) % mo);
                f[i][j+1][1][1]=add(f[i][j+1][1][1], 1ll*tmp*(p - bb[i-1])%mo);
            }
        }
        int ans = 0;
        for(int  i=a;i<=cnt2;++i)ans=add(ans,add(f[1][i][0][1],f[1][i][0][0]));
        printf("%d\n",ans);
    }


    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值