题意
组合数C(n,m)表示的是从n个物品中选出m个物品的方案数。举个例子,从(1,2,3)三个物品中选择两个物品可以有(1,2),(1,3),(2,3)这三种选择方法。根据组合数的定义,我们可以给出计算组合数C(n,m)的一般公式:
C(n,m)=n!/m!*(n?m)!其中n!=1×2×?×n。(额外的,当n=0时,n!=1)
小葱想知道如果给定n,m和k,对于所有的0≤i≤n,0≤j≤min(i,m)有多少对(i,j)满足C(i,j)是k的倍数。
题解
这题的话,要用的一个卢卡斯定理的推论
有非负整数A、B,和素数p,A、B写成p进制为:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。则组合数C(A,B)与C(a[n],b[n])×C(a[n-1],b[n-1])×...×C(a[0],b[0]) mod p同余。
证明并没有看QAQ
但是知道这个以后就可以数位DP了
显然的,因为要mod k=0,那么里面的组合数至少有一个是0
那么就可以了
具体DP状态在我代码里面的注释有写
CODE:
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const LL N=70;
const LL MOD=1e9+7;
LL T,k;
LL n,m;
LL a[N],b[N];
LL cnt;
LL f[N][2][2][2][2];
LL dfs (LL x,bool tf,bool tf1,bool tf2,bool tf3)//现在构到第几位 两个数是否一直都是一样的 是否出现了第一个a比b小的 第一个是否还有限制 第二个是否还有限制
{
if (f[x][tf][tf1][tf2][tf3]!=-1) return f[x][tf][tf1][tf2][tf3];
if (x<=0) return tf1;
LL lalal=0;
if (tf==true)//如果两个一直都是一样的,那么后面的数就不可以比前面的数大
{
if (tf2==true)//如果第一个还有限制
{
if (tf3==true)//如果第二个还有限制
{
for (LL u=0;u<=a[x];u++)//第一个数填什么
{
for (LL i=0;i<min(b[x],u);i++)
{
lalal=lalal+dfs(x-1,false,false,(u==a[x]),false);
lalal%=MOD;
}
if (u<b[x])//还是填一样的
lalal=lalal+dfs(x-1,true,false,(u==a[x]),false);
if (u==b[x])//刚好填这个
lalal=lalal+dfs(x-1,true,false,(u==a[x]),true);
if (u>b[x])
lalal=lalal+dfs(x-1,false,false,(u==a[x]),true);
lalal%=MOD;
}
}
else//第二个随便填
{
for (LL u=0;u<=a[x];u++)//第一个数填什么
for (LL i=0;i<=u;i++)
{
lalal=lalal+dfs(x-1,(i==u),false,(u==a[x]),tf3);
lalal%=MOD;
}
}
}
else
{
if (tf3==true)//第二个有限制,第一个没有
{
for (LL u=0;u<k;u++)//第一个数填什么
{
for (LL i=0;i<min(b[x],u);i++)
{
lalal=lalal+dfs(x-1,false,false,false,false);
lalal%=MOD;
}
if (u<b[x])//还是填一样的
lalal=lalal+dfs(x-1,true,false,false,false);
if (u==b[x])//刚好填这个
lalal=lalal+dfs(x-1,true,false,false,true);
if (u>b[x])
lalal=lalal+dfs(x-1,false,false,false,true);
lalal%=MOD;
}
}
else//两个都可以随便填了
{
for (LL u=0;u<k;u++)
for (LL i=0;i<=u;i++)
{
lalal+=dfs(x-1,(u==i),false,false,false);
lalal%=MOD;
}
}
}
}
else//两个数不是一直一样的
{
LL lim,lim1;
if (tf2==true) lim=a[x];
else lim=k-1;
if (tf3==true) lim1=b[x];
else lim1=k-1;
for (LL u=0;u<=lim;u++)
for (LL i=0;i<=lim1;i++)
{
lalal=lalal+dfs(x-1,false,tf1|(u<i),tf2&(u==lim),tf3&(i==lim1));
lalal%=MOD;
}
}
f[x][tf][tf1][tf2][tf3]=lalal;
/*printf("%lld %lld %lld %lld %lld %lld\n",x,tf,tf1,tf2,tf3,lalal);
system("pause");*/
return lalal;
}
int main()
{
scanf("%lld%lld",&T,&k);
while (T--)
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(f,-1,sizeof(f));
scanf("%lld%lld",&n,&m);
LL h=0;
while (n>0) {a[++h]=n%k;n/=k;}
cnt=h;h=0;
while (m>0) {b[++h]=m%k;m/=k;}
cnt=max(cnt,h);
/* for (LL u=cnt;u>=1;u--) printf("%lld ",a[u]);
printf("\n");
for (LL u=cnt;u>=1;u--) printf("%lld ",b[u]);
printf("\n");*/
printf("%lld\n",dfs(cnt,true,false,true,true));
}
return 0;
}

本文介绍了一种利用卢卡斯定理的推论解决特定组合数问题的方法,并通过数位DP实现。问题要求计算在给定n、m和k的情况下,有多少对(i, j)满足0≤i≤n且0≤j≤min(i,m),使得C(i,j)是k的倍数。
2922

被折叠的 条评论
为什么被折叠?



