清华集训2016 组合数问题

  • 【清华集训2016】组合数问题
  • 其实卢卡斯定理是一个\(K\)进制拆分的过程。
  • 考虑每一次卢卡斯迭代的过程,实际上就是把最后一位数单独拿出来算,然后把最后一位去掉。
  • 那么如果我们把两个位置上的数都拆分的话,那么就相当于每一个位置上的组合数乘起来。

  • \[C_{p,q}=\prod_{i\leq p,j\leq q} C_{i,j}\]
  • 所以问题就变成了,有多少个\(i\leq n,j\leq m\)的数对\((i,j)\),满足在\(K\)进制拆分下的某一位数\(i<j\)
  • 这个就比较套路了,直接数位\(dp\)即可,设\(f_{i,01,01,01}\)表示当前在\(i\)位,是否满足要求,\(i\)的填法是否危险,\(j\)的填法是否危险,记忆化即可。
  • 复杂度\(O(8*log_kn)\)
#include<bits/stdc++.h>
#define R register int
#define ll long long 
using namespace std;
const int N=100011;
const int mod=1e9+7;
const int inv=500000004;
int t,K,l1,l2,Dc,O1[N],O2[N],C[201][201];
int f[2][2][2][N];
ll n,m;
int gi(){
    R x=0,k=1;char c=getchar();
    while(c!='-'&&(c<'0'||c>'9'))c=getchar();
    if(c=='-')k=-1,c=getchar();
    while(c<='9'&&c>='0')x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*k;
}
void add(R &x,R y){x=(x+y>=mod?x+y-mod:x+y);}
int Dfs(R is,R q,R p,R i){
    if(!i)return is;
    if(f[is][p][q][i]!=-1)return f[is][p][q][i];
    R &nw=f[is][p][q][i],lm1=(q?(K-1):O1[i]),lm2=(p?(K-1):O2[i]);
    nw=0;
    for(R j=0;j<=lm1;++j)
        for(R k=0;k<=lm2;++k)
            add(nw,Dfs(is|(j<k),q|(j<lm1),p|(k<lm2),i-1));
    return f[is][p][q][i];
}
void sol(){
    scanf("%lld %lld",&n,&m);
    l1=l2=0,m=min(n,m),Dc=m%mod;
    memset(f,-1,sizeof(f));
    while(n)O1[++l1]=(n%K),n/=K;
    while(m)O2[++l2]=(m%K),m/=K;
    while(l2<=l1)O2[++l2]=0;
    R ans=Dfs(0,0,0,l1);
    add(ans,mod-(1ll*(Dc+1)*Dc%mod*inv%mod));
    printf("%d\n",ans);
}
int main(){
    t=gi(),K=gi();
    while(t--)sol();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Tyher/p/10051430.html
今日推荐