BZOJ4737 组合数问题(卢卡斯定理+数位dp)

  不妨不管j<=i的限制。由卢卡斯定理,C(i,j) mod k=0相当于k进制下存在某位上j大于i。容易想到数位dp,即设f[x][0/1][0/1][0/1]为到第x位时是否有某位上j>i,是否卡n、m的限制的方案数。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100
#define P 1000000007
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
ll n,m;
int T,k,ans,f[N][2][2][2],a[N],b[N];
void inc(int &x,int y){x+=y;if (x>=P) x-=P;}
int calc(ll n,ll m)
{
    int t=-1;
    while (n) a[++t]=n%k,n/=k;
    for (int i=0;i<=t;i++) b[i]=m%k,m/=k;
    memset(f,0,sizeof(f));
    f[t+1][0][1][1]=1;
    for (int i=t;~i;i--)
        for (int j=0;j<=1;j++)
            for (int x=0;x<=1;x++)
                for (int y=0;y<=1;y++)
                    for (int p=x;p<=1;p++)
                        for (int q=y;q<=1;q++)
                        {
                            int ln=x==1?a[i]:0,rn=x==1?a[i]:(p==1?a[i]-1:k-1),lm=y==1?b[i]:0,rm=y==1?b[i]:(q==1?b[i]-1:k-1);
                            int s=0;
                            for (int u=ln;u<=rn;u++)
                                for (int v=lm;v<=rm;v++)
                                if (v<=u) s++;
                            inc(f[i][j][x][y],1ll*f[i+1][j][p][q]*s%P);
                            if (j) inc(f[i][j][x][y],1ll*f[i+1][j-1][p][q]*((rn-ln+1)*(rm-lm+1)-s)%P),
                            inc(f[i][j][x][y],1ll*f[i+1][j][p][q]*((rn-ln+1)*(rm-lm+1)-s)%P);
                        }
    return ((f[0][1][0][0]+f[0][1][0][1])%P+(f[0][1][1][0]+f[0][1][1][1])%P)%P;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4737.in","r",stdin);
    freopen("bzoj4737.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    T=read(),k=read();
    while (T--)
    {
        cin>>n>>m;m=min(n,m);
        if (m&1) ans=(P-(m%P)*((m+1>>1)%P)%P)%P;
        else ans=(P-((m+1)%P)*((m>>1)%P)%P)%P;
        inc(ans,calc(n,m));
        cout<<ans<<endl;
    }
    return 0;
}

 

posted @ 2018-11-14 00:37  Gloid  阅读(295)  评论(0编辑  收藏  举报