UOJ275[清华集训2016]组合数问题



组合数 Cmn C n m 表示的是从 n n 个物品中选出 m 个物品的方案数。举个例子,从 (1,2,3) ( 1 , 2 , 3 ) 三个物品中选择两个物品可以有 (1,2),(1,3),(2,3) ( 1 , 2 ) , ( 1 , 3 ) , ( 2 , 3 ) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 Cmn C n m 的一般公式:

Cmn=n!m!(nm)! C n m = n ! m ! ( n − m ) !

其中 n!=1×2××n n ! = 1 × 2 × ⋯ × n 。(额外的,当 n=0 n = 0 时, n!=1 n ! = 1

小葱想知道如果给定 n,m n , m k k ,对于所有的 0in,0jmin(i,m) 有多少对 (i,j) ( i , j ) 满足 Cji C i j k k 的倍数。

答案对 109+7 取模。

输入格式

第一行有两个整数 t,k t , k ,其中 t t 代表该测试点总共有多少组测试数据。

接下来 t 行每行两个整数 n,m n , m

输出格式

t t 行,每行一个整数代表所有的 0in,0jmin(i,m) 中有多少对 (i,j) ( i , j ) 满足 Cji C i j k k 的倍数。

样例一

input

1 2
3 3

output

1

explanation

在所有可能的情况中,只有 C21=2 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

限制与约定

对于 20% 的测试点, 1n,m100 1 ≤ n , m ≤ 100

对于另外 15% 15 % 的测试点, nm n ≤ m

对于另外 15% 15 % 的测试点, k=2 k = 2

对于另外 15% 15 % 的测试点, m10 m ≤ 10

对于 100% 100 % 的测试点, 1n,m1018,1t,k100 1 ≤ n , m ≤ 10 18 , 1 ≤ t , k ≤ 100 ,且 k k 是一个质数。

时间限制1s

空间限制 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;
    }
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值