GDSOI2019 D2 高中生数学题 (计算组合数中的质数幂次、库默尔定理、数位dp)

在这里插入图片描述在这里插入图片描述
库默尔定理:
C ( n , m ) C(n,m) C(n,m)中含有的质数p幂次
= n − m n - m nm在p进制下的借位次数
= m + ( n − m ) m + (n - m) m+(nm)在p进制下的进位次数。

原理:
A n s = ∑ k > 0 n p k − ( n − m p k + m p k ) Ans = \sum_{k>0}\frac n {p^k}-(\frac{n-m}{p^k} + \frac{m}{p^k}) Ans=k>0pkn(pknm+pkm)
(都是整除)
即:(a+b)从 p k p^k pk起的高位与 a 的 高 位 + b 的 高 位 a的高位+b的高位 a+b是否相等,取决于该位是否接收了低位的进位。(因此最多差1)

由此,进位次数便与所含幂次扯上了关系。数位dp常用来统计这类数的个数。

以下代码是暴力dp。仅需要添加一些讨论即可获得正解。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 70;

ll n,p,K;
ll g[N];
ll f[N][N][2][2];

int main(){
	freopen("math.in","r",stdin);
	// freopen("math.out","w",stdout);
	cin>>n>>p>>K;
	while(n) {
		g[++g[0]] = n % p;
		n /= p;
	}
	f[g[0]+1][0][0][1] = 1;
	for(int i = g[0]; i; i--) {
		for(int cnt = 0; cnt < g[0]; cnt++) {
			for(int li = 0; li < 2; li++) {
				for(int jw = 0; jw < 2; jw++) if (f[i + 1][cnt][jw][li]) {
					for(int njw = 0; njw < 2; njw++) {
						if(njw==1 && i==1) break;
						for(int x = 0; x < p; x++) {
							if(li && x > g[i]) break;
							int nli = li && x == g[i];
							int ts = g[i] + (jw ? p : 0) - njw;
							if(ts - x >= p || ts - x < 0) continue;
							f[i][cnt + njw][njw][nli] += f[i + 1][cnt][jw][li];
						}
					}
				}
			}
		}
	}
	ll ans = 0;
	for(int z = K; z < g[0]; z++) ans += f[1][z][0][1] + f[1][z][0][0];
	printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值