[bzoj4737][數論][DP]組合數問題
阿新 • • 發佈:2018-12-09
Description
組合數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的倍數。
Input
第一行有兩個整數t,k,其中t代表該測試點總共有多少組測試資料,k的意義見。 接下來t行每行兩個整數n,m,其中n,m的意義見。
Output
t行,每行一個整數代表所有的0≤i≤n,0≤j≤min(i,m)中有多少對(i,j))滿足C(i,j)是k的倍數
答案對10^9+7取模。
Sample Input
3 23
23333333 23333333
233333333 233333333
2333333333 2333333333
Sample Output
851883128
959557926
680723120
HINT
1≤n,m≤10^18,1≤t,k≤100,且 k 是一個質數’
題解
個個都說很
顯然我辣雞當然不覺得考慮lucas定理 這裡p就等於k了嘛.. 其實就相當於把n,m寫成p進位制,設b1[u]表示n在p進位制下的第u位,b2[u]表示m在p進位制下的第u位 如果有至少一位i使得b1[i] < b2[i] 那麼這個數mod p是為0的 於是就可以愉快數位dp了實現怎麼這麼複雜啊..
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
const LL mod=1e9+7;
int T,k;LL n,m,ans;
int s1[1100],s2[1100],ln1,ln2;
void get(LL u,int op)
{
if(op==1)
{
ln1=0;
while(u)s1[++ln1]=u%k,u/=k;
}
else
{
ln2=0;
while(u)s2[++ln2]=u%k,u/=k;
}
}
LL pow_mod(LL a,LL b)
{
if(!a)return 0;
LL ret=1;
while(b)
{
if(b&1)ret=ret*a%mod;
a=a*a%mod;b>>=1;
}
return ret;
}
void ad(LL &x,LL y){x+=y;if(x>mod)x-=mod;}
LL f[70][2][2][2][2];
LL dp(int p,int op1,int op2,int op3,int op4)//位置 是否卡上界 是否出現第一個m比n大的數 是否出現第一個m比n小的數
{
if(f[p][op1][op2][op3][op4]!=-1)return f[p][op1][op2][op3][op4];
if(p<=0)return op4&op3;
LL ret=0;
// if(!op1&&!op2&&op3&&op4)return f[p][op1][op2][op3][op4]=pow_mod(k,p);
if(!op4)//這一位不能大於
{
if(op1)//m限制
{
if(op2)//n限制
{
for(int i=0;i<s1[p];i++)
{
if(i<s2[p])ad(ret,dp(p-1,0,0,op3,0));
for(int j=i+1;j<s2[p];j++)ad(ret,dp(p-1,0,0,op3,1));
if(i<s2[p])ad(ret,dp(p-1,0,1,op3,1));
else if(i==s2[p])ad(ret,dp(p-1,0,1,op3,0));
}
if(s1[p]<s2[p])ad(ret,dp(p-1,1,0,op3,0));
for(int j=s1[p]+1;j<s2[p];j++)ad(ret,dp(p-1,1,0,op3,1));
if(s1[p]<s2[p])ad(ret,dp(p-1,1,1,op3,1));
else if(s1[p]==s2[p])ad(ret,dp(p-1,1,1,op3,0));
return f[p][op1][op2][op3][op4]=ret;
}
else//n不限制
{
for(int i=0;i<s1[p];i++)
{
if(i<k)ad(ret,dp(p-1,0,0,op3,0));
for(int j=i+1;j<k;j++)ad(ret,dp(p-1,0,0,op3,1));
}
if(s1[p]<k)ad(ret,dp(p-1,1,0,op3,0));
for(int j=s1[p]+1;j<k;j++)ad(ret,dp(p-1,1,0,op3,1));
return f[p][op1][op2][op3][op4]=ret;
}
}
else//m不限制
{
if(op2)//n限制
{
for(int i=0;i<k;i++)
{
if(i<s2[p])ad(ret,dp(p-1,0,0,op3,0));
for(int j=i+1;j<s2[p];j++)ad(ret,dp(p-1,0,0,op3,1));
if(i<s2[p])ad(ret,dp(p-1,0,1,op3,1));
else if(i==s2[p])ad(ret,dp(p-1,0,1,op3,0));
}
return f[p][op1][op2][op3][op4]=ret;
}
else//n不限制
{
for(int i=0;i<k;i++)
{
if(i<k)ad(ret,dp(p-1,0,0,op3,0));
for(int j=i+1;j<k;j++)ad(ret,dp(p-1,0,0,op3,1));
}
return f[p][op1][op2][op3][op4]=ret;
}
}
}
else
{
int l1,l2;
if(op1)l1=s1[p];else l1=k-1;
if(op2)l2=s2[p];else l2=k-1;
for(int i=0;i<=l1;i++)
for(int j=0;j<=l2;j++)
ad(ret,dp(p-1,op1&(i==l1),op2&(j==l2),op3|(i>j),1));
return f[p][op1][op2][op3][op4]=ret;
}
}
int main()
{
scanf("%d%d",&T,&k);
while(T--)
{
scanf("%lld%lld",&n,&m);
memset(f,-1,sizeof(f));
if(m>n)m=n;
get(m,1);get(n,2);
for(int i=ln1+1;i<=ln2;i++)s1[i]=0;
ln1=ln2;ans=0;
printf("%lld\n",dp(ln1,1,1,0,0));
}
return 0;
}