#275. 【清华集训2016】组合数问题
组合数 Cmn C n m 表示的是从 n n 个物品中选出 个物品的方案数。举个例子,从 (1,2,3) ( 1 , 2 , 3 ) 三个物品中选择两个物品可以有 (1,2),(1,3),(2,3) ( 1 , 2 ) , ( 1 , 3 ) , ( 2 , 3 ) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 Cmn C n m 的一般公式:
其中 n!=1×2×⋯×n n ! = 1 × 2 × ⋯ × n 。(额外的,当 n=0 n = 0 时, n!=1 n ! = 1 )
小葱想知道如果给定 n,m n , m 和 k k ,对于所有的 有多少对 (i,j) ( i , j ) 满足 Cji C i j 是 k k 的倍数。
答案对 取模。
输入格式
第一行有两个整数 t,k t , k ,其中 t t 代表该测试点总共有多少组测试数据。
接下来 行每行两个整数 n,m n , m 。
输出格式
t t 行,每行一个整数代表所有的 中有多少对 (i,j) ( i , j ) 满足 Cji C i j 是 k k 的倍数。
样例一
input
1 2 3 3
output
1
explanation
在所有可能的情况中,只有 是 2 2 的倍数。
样例二
input
2 5 4 5 6 7
output
0 7
样例三
input
3 23 23333333 23333333 233333333 233333333 2333333333 2333333333
output
851883128 959557926 680723120
限制与约定
对于 的测试点, 1≤n,m≤100 1 ≤ n , m ≤ 100 ;
对于另外 15% 15 % 的测试点, n≤m n ≤ m ;
对于另外 15% 15 % 的测试点, k=2 k = 2 ;
对于另外 15% 15 % 的测试点, m≤10 m ≤ 10 ;
对于 100% 100 % 的测试点, 1≤n,m≤1018,1≤t,k≤100 1 ≤ n , m ≤ 10 18 , 1 ≤ t , k ≤ 100 ,且 k k 是一个质数。
时间限制:
空间限制: 512MB 512 MB
分析&&思路:
这道题可以根据Lucas定理,把 Cmn C n m 是k的倍数转化为n,m为k进制数,某一位的组合数也是k的倍数也就是n,m在k进制下有一位比k小,之后通过数位dp转化一下就行了。
Code
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const ll mod=1e9+7,inv2=(mod+1)>>1;
int T,n,m;
ll f[70][2][2],a[70],b[70],k,x,y;
inline ll calc(ll x,ll y) {
if(x<0||y<0)
return 0;
if(x<y)
return (((x+2)%mod)*((x+1)%mod))%mod*inv2%mod;
return (((((y+2)%mod)*((y+1)%mod))%mod*inv2%mod+(((x-y)%mod)*((y+1)%mod))%mod)%mod+mod)%mod;
}
int main() {
read(T),read(k);
while(T--) {
read(x);n=m=0;
ll tmp=x;
while(tmp) {
a[++n]=tmp%k;
tmp/=k;
}
read(y);
y=min(y,x);
tmp=y;
while(tmp) {
b[++m]=tmp%k;
tmp/=k;
}
ll ans=calc(x,y);
f[0][1][1]=1;
for(int i=1;i<=n;i++) {
f[i][1][1]=(calc(a[i],b[i])*f[i-1][1][1]%mod+calc(a[i],b[i]-1)*f[i-1][1][0]%mod+calc(a[i]-1,b[i])*f[i-1][0][1]%mod+calc(a[i]-1,b[i]-1)*f[i-1][0][0]%mod)%mod;
f[i][0][1]=(calc(k-1,b[i])*((f[i-1][1][1]+f[i-1][0][1])%mod)%mod+(calc(k-1,b[i]-1)*((f[i-1][1][0]+f[i-1][0][0])%mod)%mod-f[i][1][1]+mod)%mod)%mod;
f[i][1][0]=(calc(a[i],k-1)*((f[i-1][1][1]+f[i-1][1][0])%mod)%mod+(calc(a[i]-1,k-1)*((f[i-1][0][1]+f[i-1][0][0])%mod)%mod-f[i][1][1]+mod)%mod)%mod;
f[i][0][0]=(((calc(k-1,k-1)*(f[i-1][1][1]+f[i-1][1][0]+f[i-1][0][1]+f[i-1][0][0])%mod-f[i][1][1]+mod)%mod-f[i][0][1]+mod)%mod-f[i][1][0]+mod)%mod;
}
printf("%lld\n",(ans-f[n][1][1]+mod)%mod);
while(n)
a[n--]=0;
while(m)
b[m--]=0;
}
}