1. 程式人生 > >[bzoj4737][數論][DP]組合數問題

[bzoj4737][數論][DP]組合數問題

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定理

Cnm=Cn/pm/pCnmodpmmodp(modp) 這裡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; }