(貌似是远古大坑了
T1 bzoj 4737 组合数问题
题目大意:
给定 $n,m$ 求有多少个$C_n^m$整除$k$ $(n,m \le 10^{18})$
思路:
考虑如何计算组合数 使用lucas只有在lucas递归过程中n<m才会产生0
因此我们考虑把$n,m$按照k进制分解 只要中间某一位n<m即满足题意
可以数位dp 由于统计补集比较简单 所以统计补集即中间所有数$m \le n$
$dp \space i \space j \space k$表示到了第i位 是否卡在了$n,m$的边界上 转移非常好转移
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<vector> 9 #include<map> 10 #define ll long long 11 #define inf 2139062143 12 #define rep(i,s,t) for(int i=(s);i<=(t);++i) 13 #define dwn(i,s,t) for(int i=(s);i>=(t);--i) 14 #define ren for(int i=fst[x];i;i=nxt[i]) 15 #define Fill(x,t) memset(x,t,sizeof(x)) 16 #define MAXN 100 17 #define MOD 1000000007 18 using namespace std; 19 inline ll read() 20 { 21 ll x=0,f=1;char ch=getchar(); 22 while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();} 23 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 24 return x*f; 25 } 26 ll n,m,T,p,dp[MAXN][2][2],a[MAXN],la,b[MAXN],lb,ans; 27 ll q_pow(ll bas,ll t) 28 { 29 ll res=1; 30 for(;t;t>>=1,(bas*=bas)%=MOD) 31 if(t&1) (res*=bas)%=MOD; 32 return res; 33 } 34 inline ll inv(ll x) {return q_pow(x,MOD-2);} 35 int main() 36 { 37 T=read(),p=read();ll inv2=inv(2);int lx,ly; 38 while(T--) 39 { 40 n=read(),m=read(),m=min(n,m),la=lb=0LL;Fill(dp,0);Fill(a,0);Fill(b,0); 41 ans=(((((m%MOD)*((m+1)%MOD))%MOD)*inv2)%MOD+(((n-m+1)%MOD)*((m+1)%MOD))%MOD)%MOD; 42 while(n) a[++la]=n%p,n/=p;while(m) b[++lb]=m%p,m/=p; 43 dp[la+1][1][1]=1; 44 dwn(i,la,1) rep(j,0,1) rep(k,0,1) 45 if(dp[i+1][j][k]) 46 { 47 if(j) lx=a[i];else lx=p-1; 48 if(k) ly=b[i];else ly=p-1; 49 rep(x,0,lx) rep(y,0,min(ly,x)) (dp[i][j&(x==a[i])][k&(y==b[i])]+=dp[i+1][j][k])%=MOD; 50 } 51 rep(i,0,1) rep(j,0,1) (ans+=MOD-dp[1][i][j])%=MOD; 52 printf("%lld\n",ans); 53 } 54 }