1245 Binomial Coefficients Revenge
题目来源: HackerRank
基准时间限制:2 秒 空间限制:131072 KB 分值: 640 难度:8级算法题 收藏 关注
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的倍数……。
例如:M = 10, P = 2。C(10,0) -> C(10,10)
分别为:1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1。
P的幂 = 1 2 4 8 16……
1 45 45 1 这4个数只能整除1。
10 210 210 10这4个数能整除2但不能整除4。
252 能整除4但不能整除8。
120 120 这2个数能整除8但不能整除16。
所以输出:4 4 1 2。
Input
第1行:一个数T,表示输入的测试数量(1 <= T <= 5000)
第2 - T + 1行:每行2个数,M和P,中间用空格分隔(2 <= M, P <= 10^18)
Output
输出共T行,每行若干个数,中间用空格分隔,对应组合数的数量。
Input示例
3
4 5
6 3
10 2
Output示例
5
3 4
4 4 1 2
【分析】
有一个比较奇葩的定理叫做Kummer定理
设m,n为正整数,p为素数,则
C(m+n,m)
含p的幂次等于m+n在p进制下的进位次数。
所以这题就是给定n,要求对于每一个m,C(n-m)在p进制下的进位次数
于是我们把n用p进制表示,
dp[i][j][0/1]
表示在n的p进制下从低位往高位的第i位,总共进位了j次,当前数<=n(0)还是>n(1)时的贡献。
pj 的答案就是 dp[cnt][j][0]
【代码】
//51nod
#include<bits/stdc++.h>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=105;
ll T,n,p;
ll dp[mxn][mxn][2],digit[mxn];
int main()
{
int i,j,k,cnt;
scanf("%lld",&T);
while(T--)
{
cnt=0,M(dp);
scanf("%lld%lld",&n,&p);
while(n) digit[++cnt]=n%p,n/=p;
dp[1][0][0]=digit[1]+1;
dp[1][1][1]=p-digit[1]-1;
fo(i,1,cnt-1)
{
ll x=digit[i+1];
fo(j,0,cnt)
{
dp[i+1][j][0]+=(x+1)*dp[i][j][0]+x*dp[i][j][1];
dp[i+1][j+1][1]+=(p-x-1)*dp[i][j][0]+(p-x)*dp[i][j][1];
}
}
for(k=cnt;dp[cnt][k][0]==0;k--);
fo(j,0,k) printf("%lld ",dp[cnt][j][0]);
printf("\n");
}
return 0;
}