@description@
C(M,N) = M! / N! / (M - N)! (组合数)。
给出M和质数p,求C(M,0), C(M,1)......C(M,M)这M + 1个数中,有多少数不是p的倍数,有多少是p的倍数但不是p^2的倍数,有多少是p^2的倍数但不是p^3的倍数......。
@solution@
考虑一个阶乘 n! 含多少 p 因子,容易发现含 (sum_{i=1}lfloorfrac{n}{p^i} floor) 个。
因此,组合数 C(M, N) 含因子 p 共 (sum_{i=1}(lfloorfrac{M}{p^i} floor - lfloorfrac{N}{p^i} floor - lfloorfrac{M - N}{p^i} floor)) 个。
而上式又可以写作 (sum_{i=1}[N mod p^i + (M - N) mod p^i geq M mod p^i])。或者更通俗地说,N + (M - N) 在 p 进制下的进位次数。
因此直接从低位开始数位 dp,记录一下上一次有没有进位即可。
@accepted code@
#include <cstdio>
#include <cassert>
#include <algorithm>
using namespace std;
typedef long long ll;
ll dgt[66], M, P; int cnt;
void get_dight() {
cnt = 0;
for(ll i=M;i;i/=P)
dgt[++cnt] = i % P;
}
ll fun(ll x) {
return max(0LL, min(P - 1, x) - max(0LL, x - P + 1) + 1);
}
ll f[2][66], g[2][66];
void solve() {
scanf("%lld%lld", &M, &P), get_dight();
f[0][0] = 1, f[1][0] = 0;
for(int i=1;i<=cnt;i++) {
for(int j=0;j<i;j++)
for(int o=0;o<=1;o++)
g[o][j] = f[o][j], f[o][j] = 0;
f[0][i] = f[1][i] = 0;
for(int o=0;o<=1;o++) {
ll p = dgt[i] - o;
for(int j=0;j<i;j++) f[0][j] += fun(p)*g[o][j];
p = dgt[i] + P - o;
for(int j=0;j<i;j++) f[1][j + 1] += fun(p)*g[o][j];
}
}
while( f[0][cnt - 1] == 0 ) cnt--;
ll sum = 0;
for(int i=0;i<cnt;i++)
printf("%lld%c", f[0][i], i + 1 == cnt ? '
' : ' '), sum += f[0][i];
assert(sum == M + 1);
}
int main() {
int T; scanf("%d", &T);
while( T-- ) solve();
}
@details@
51nod 搬题的时候并没有附上原题关于输出格式的说明。事实上本题需要舍去末尾多余的 0。
本题所用的结论其实叫作 “库默尔定理”,不过定理名字也不是很重要。