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; }