[BZOJ4737][lucas]清华集训2016:组合数问题

BZOJ4737

lucas定理的简单证明:
C n m % p = C n / p m / p ∗ C n % p m % p % p C_n^m\%p=C_{n/p}^{m/p}*C_{n\%p}^{m\%p}\%p Cnm%p=Cn/pm/pCn%pm%p%p
首先有 ( 1 + x ) p ≡ 1 + x p ( % p ) (1+x)^p \equiv 1+x^p (\%p) (1+x)p1+xp(%p),由费马小定理易证
( 1 + x ) p ≡ ( 1 + x ) ⌊ n / p ⌋ ∗ p ( 1 + x ) r (1+x)^p\equiv(1+x)^{\lfloor{n/p}\rfloor*p}(1+x)^r (1+x)p(1+x)n/pp(1+x)r
≡ ( 1 + x p ) ⌊ n / p ⌋ ( 1 + x ) r \equiv (1+x^p)^{\lfloor{n/p}\rfloor}(1+x)^r (1+xp)n/p(1+x)r
二项式定理: ≡ ∑ i = 0 k C ⌊ n p ⌋ i x p i ∑ i = 0 k C r j x j \equiv \sum_{i=0}^k{C_{\lfloor{\frac{n}{p}}\rfloor}^i}x^{pi}\sum_{i=0}^k{C_r^j}x^j i=0kCpnixpii=0kCrjxj
左边第m项系数为 C n m C_n^m Cnm
右边第m项系数为 C ⌊ n p ⌋ i C r j ( p i + j = m , j < p ) C_{\lfloor{\frac{n}{p}}\rfloor}^i C_r^j(pi+j=m,j<p) CpniCrj(pi+j=m,j<p)
p i + j = m pi+j=m pi+j=m看作除法的形式即可得证

对于本题,我们将i,j看作k进制数,则要求i,j的每一位分别构成的组合数之积模k=0
因为是k进制数,且k为质数,所以这样的组合数不存在k的因子
则必须要求存在某一位,该位上i的数字小于j的数字,这样这一位的组合数就为0
则数位dp即可

Code:

#include<bits/stdc++.h>
#define mod 1000000007
#define ll long long 
using namespace std;
inline ll read(){
	ll res=0,ff=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') ff=-ff;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*ff; 
}
const int N=1e5+5,inv2=5e8+4;
int t,k;
int l1,l2,lim,p1[N],p2[N],c[205][205],f[N][2][2][2];
ll m,n;
inline void add(int &x,int y){x+=y;if(x>=mod) x-=mod;}
int dfs(int flag,int q,int p,int now){
	if(!now) return flag;
	if(f[now][flag][p][q]!=-1) return f[now][flag][p][q];
	int lim1=(q?(k-1):p1[now]),lim2=(p?(k-1):p2[now]);
	int &tmp=f[now][flag][p][q];
	tmp=0;
	for(int i=0;i<=lim1;i++)
		for(int j=0;j<=lim2;j++)
			add(tmp,dfs(flag|(i<j),q|(i<lim1),p|(j<lim2),now-1));
	return f[now][flag][p][q];
}
int main(){
	t=read();k=read();
	while(t--){
		n=read();m=read();
		l1=l2=0;m=min(n,m);lim=m%mod;
		memset(f,-1,sizeof(f));
		while(n) p1[++l1]=(n%k),n/=k;
		while(m) p2[++l2]=(m%k),m/=k;
		while(l2<=l1) p2[++l2]=0;
		int ans=dfs(0,0,0,l1);
		add(ans,mod-(1ll*(lim+1)*lim%mod*inv2%mod));
		cout<<ans<<"\n";
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值