1. 程式人生 > >uoj#275. 【清華集訓2016】組合數問題(數位dp)

uoj#275. 【清華集訓2016】組合數問題(數位dp)

傳送門

假設有\(k|{n\choose m}\),因為\(n!\)中質因子\(k\)的次數為\(S(n)=\left\lfloor\frac{n}{k}\right\rfloor+\left\lfloor\frac{n}{k^2}\right\rfloor+...\),而\(m!\)\((n-m)!\)同理。所以如果\(S(n)>S(m)+S(n-m)\),那麼\(k|{n\choose m}\)

不難發現,對於每一個\(k^i\)\(\left\lfloor\frac{n}{k^i}\right\rfloor\geq \left\lfloor\frac{m}{k^i}\right\rfloor+\left\lfloor\frac{n-m}{k^i}\right\rfloor\)

。所以只要有一個\(i\)使得\(\left\lfloor\frac{n}{k^i}\right\rfloor>\left\lfloor\frac{m}{k^i}\right\rfloor+\left\lfloor\frac{n-m}{k^i}\right\rfloor\),那麼\(S(n)>S(m)+S(n-m)\)

\(i=1\)的時候,設\(n=ak+b,m=ck+d\),則\(n-m=(a-c)k+b-d\),那麼如果\(b-d<0\)\(\left\lfloor\frac{n}{k}\right\rfloor=a,\left\lfloor\frac{m}{k}\right\rfloor+\left\lfloor\frac{n-m}{k}\right\rfloor=c+(a-c-1)=a-1<a\)

,那麼就有\(S(n)>S(m)+S(n-m)\)

同理可得,若\(n\)\(m\)\(k\)進製表示下第\(i\)位滿足\(n_i<m_i\),那麼就有\(S(n)>S(m)+S(n-m)\)

於是現在的問題就是變成了求\(i<n,j<m\)\(i\)\(k\)進製表示下有某一位數值比\(j\)小,可以數位dp,這個就不講了

然後上面的情況下我們沒有考慮\(j>i\)的情況,因為如果\(j>i\)那麼\(k\)進位制下\(i\)肯定有某一位小於\(j\),所以我們對於每個\(i\)算出它會多算的個數,發現就是一個等差數列求和的形式,帶公式就好了

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
    R ll res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=65,P=1e9+7,inv=500000004;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
    return res;
}
int l1,l2,num1[N],num2[N],k,p,ans;
int f[2][2][2][N];ll n,m;
int dfs(int p,int q,int ok,int pos){
    if(!pos)return ok;
    if(~f[p][q][ok][pos])return f[p][q][ok][pos];
    int res=0,lm1=p?k-1:num1[pos],lm2=q?k-1:num2[pos];
    fp(i,0,lm1)fp(j,0,lm2)
            res=add(res,dfs(p|(i<lm1),q|(j<lm2),ok|(i<j),pos-1));
    return f[p][q][ok][pos]=res;
}
void solve(){
    n=read(),m=read(),l1=l2=0,m=min(n,m),p=m%P;
    memset(f,-1,sizeof(f));
    while(n)num1[++l1]=n%k,n/=k;
    while(m)num2[++l2]=m%k,m/=k;
    while(l2<=l1)num2[++l2]=0;
    ans=dfs(0,0,0,l1);
    ans=dec(ans,1ll*(p+1)*p%P*inv%P);
    printf("%d\n",ans);
}
int main(){
//  freopen("testdata.in","r",stdin);
    int T=read();k=read();
    while(T--)solve();
    return 0;
}