UOJ 275. 【清华集训2016】组合数问题
组合数 $C_n^m $表示的是从 (n) 个物品中选出 (m) 个物品的方案数。举个例子,从$ (1,2,3)(1,2,3)$ 三个物品中选择两个物品可以有 ((1,2),(1,3),(2,3)) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数$ C_m^n$的一般公式:
[C_n^m=frac{n!}{m!(n-m)!}
]
其中 (n!=1×2×⋯×n)。(额外的,当 n=0n=0 时, n!=1n!=1)
小葱想知道如果给定$ n,m$ 和 (k),对于所有的 (0≤i≤n,0≤j≤min{i,m})有多少对 ((i,j)) 满足 (C_i^j)是 (k) 的倍数。
答案对 (10^9+7) 取模。
输入格式
第一行有两个整数 (t,k)其中 (t) 代表该测试点总共有多少组测试数据。
接下来 (t) 行每行两个整数 (n,m)。
输出格式
(t) 行,每行一个整数代表所有的 (0leq ileq n,0leq jleq min left { i, m ight }) 中有多少对$ (i,j)(满足)C_i^j$是 (k) 的倍数。
限制与约定
对于(100\%) 的测试点, (1leq n,mleq 10^{18},1 leq t,kleq 100),且 (k) 是一个质数。
(\)
首先考虑使用卢卡斯定理:
[ ext{Lucas}(n,m)mod k= ext{Lucas}(frac{n}{k},frac{m}{k})cdotinom{n\%k}{m\%k}mod k
]
迭代过程中只要有一位上的(inom{n\%k}{m\%k}=0)那么最后的组合数就是(k)的倍数。当(n<k,m<k)时,只有(n<m)的情况下:(inom{n}{m}=0)。
我们将(n,m)写成(k)进制的数,然后做数位(DP)。先不考虑(jleq i)的限制的话要好做一些,然后在减掉(j>i)的情况(这部分显然为0)就好了。
代码(小心爆(long long)):
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
const ll mod=1e9+7;
ll n,m,k;
ll A[100],B[100];
int d;
#define pr pair<ll,ll>
#define mp(a,b) make_pair(a,b)
pr f[100][2][2];
pr dfs(int v,int flag1,int flag2) {
if(v<0) return mp(0,1);
if(f[v][flag1][flag2].first!=-1) return f[v][flag1][flag2];
int u1=(!flag1)?k-1:A[v],u2=(!flag2)?k-1:B[v];
ll ans0=0,ans1=0;
pr now;
for(int i=0;i<=u1;i++) {
for(int j=0;j<=u2;j++) {
now=dfs(v-1,flag1&&i==u1,flag2&&j==u2);
if(i<j) {
(ans0+=1ll*now.first+now.second)%=mod;
} else {
(ans0+=now.first)%=mod;
(ans1+=now.second)%=mod;
}
}
}
f[v][flag1][flag2]=mp(ans0,ans1);
return mp(ans0,ans1);
}
ll cal(ll l,ll r) {return 1ll*(l+r)*(r-l+1)/2%mod;}
int main() {
int T=Get();
k=Get();
while(T--) {
n=Get(),m=Get();
d=0;
ll mx=max(n,m);
while(mx) {
d++;
mx/=k;
}
d--;
ll x=n;
for(int i=0;i<=d;i++) {
A[i]=x%k;
x/=k;
}
x=m;
for(int i=0;i<=d;i++) {
B[i]=x%k;
x/=k;
}
for(int i=0;i<=d;i++)
for(int a=0;a<=1;a++)
for(int b=0;b<=1;b++) f[i][a][b]=mp(-1,-1);
pr ans=dfs(d,1,1);
ans.first=(ans.first-cal(max(1ll,m-n)%mod,m%mod)+mod)%mod;
cout<<ans.first<<"
";
}
return 0;
}