Codeforces 582D - Number of Binominal Coefficients(Kummer 定理+数位 dp)

Codeforces 题目传送门 & 洛谷题目传送门

一道数论与数位 dp 结合的神题 %%%

首先在做这道题之前你需要知道一个定理:对于质数 \(p\)\(n,k\),最大的满足 \(p^{\alpha}\mid\dbinom{n}{k}\)\(\alpha\)\(k\)\(n-k\)\(p\) 进制下相加的进位次数。证明就考虑扩展 Lucas 定理,记 \(f(x)\) 为最大的满足 \(p^{\alpha}\mid x\)\(\alpha\),那么由 \(\dbinom{n}{k}=\dfrac{n!}{k!(n-k)!}\) 可知 \(f(\dbinom{n}{k})=f(n!)-f(k!)-f((n-k)!)\),我们考虑将 \(k,n-k\) 都在 \(k\) 进制下表示,我们设它们从高到低分别是 \(k=(a_{m-1}a_{m-2}\cdots a_1a_0)_p,n-k=(b_{m-1}b_{m-2}\cdots b_1b_0)_p,n=(c_{m-1}c_{m-2}\cdots c_1c_0)_p\)(在这里,我们不妨假设 \(n<p^m\),即 \(n+k\)\(p^m\) 位不会产生进位),那么根据扩展 Lucas 定理 \(f(k!)\) 应当为 \(\sum\limits_{i=1}^{m-1}(a_{m-1}a_{m-2}\cdots a_i)_p\),即 \(k\)\(p\) 进制表示下所有前缀(不包括本身)表示的数之和。很显然的一点是对于任意 \(i\in[1,m-1]\)\((a_{m-1}a_{m-2}\cdots a_i)_p+(b_{m-1}b_{m-2}\cdots b_i)_p\le(c_{m-1}c_{m-2}\cdots c_i)_p\),那么什么时候 \((a_{m-1}a_{m-2}\cdots a_i)_p+(b_{m-1}b_{m-2}\cdots b_i)_p<(c_{m-1}c_{m-2}\cdots c_i)_p\) 呢?显然如果前面没有进位那就不可能存在这种情况,否则,由于只有两数相加,因此最多进上来一位,而进上来一位以后显然就有 \((a_{m-1}a_{m-2}\cdots a_i)_p+(b_{m-1}b_{m-2}\cdots b_i)_p+1=(c_{m-1}c_{m-2}\cdots c_i)_p\),会对答案产生 \(1\) 的贡献,因此该结论成立。据说该结论有一个名字叫什么库默尔(Kummer)定理,不过名字啥的不重要啦((

知道这个结论之和就可以数位 dp 了。题目中 \(\alpha\le 10^9\) 是假的,如果 \(\alpha>\log_pA\) 那答案显然为 \(0\)。我们首先将题目给出的那个数用 \(p\) 进制表示,我们设 \(dp_{i,j,0/1,0/1}\) 表示考虑了最高的 \(i\) 位,当前进位了 \(j\) 次,上一位(第 \(i+1\) 高的位)是否产生进位,当前是否达到上界,考虑转移,假设 \(A\) 的第 \(i+1\) 位的值为 \(c\),我们要决策 \(k\) 的第 \(i+1\) 位的值 \(a\)\(n-k\) 的第 \(i+1\) 位的值 \(b\),那么有转移:

  • \(dp_{i+1,j,0,0}\)
    • 如果从 \(dp_{i,j,0,0}\) 转移来那么需满足 \(a+b<p\),方案数 \(\dfrac{p(p+1)}{2}\)
    • 如果从 \(dp_{i,j,0,1}\) 转移来那么需满足 \(a+b<c\),方案数 \(\dfrac{c(c+1)}{2}\)
    • 如果从 \(dp_{i,j,1,0}\) 转移来那么需满足 \(a+b\ge p\),方案数 \(\dfrac{p(p-1)}{2}\)
    • 如果从 \(dp_{i,j,1,1}\) 转移来那么需满足 \(p\le a+b<p+c\),方案数 \(\dfrac{(p+c)(p+c+1)}{2}-\dfrac{p(p+1)}{2}=\dfrac{c(2n-c-1)}{2}\)
  • \(dp_{i+1,j,0,1}\)
    • 如果从 \(dp_{i,j,0,1}\) 转移来那么需满足 \(a+b=c\),方案数 \(c+1\)
    • 如果从 \(dp_{i,j,1,1}\) 转移来那么需满足 \(a+b=p+c\),方案数 \(p-c-1\)
  • \(dp_{i+1,j,1,0}\)
    • 如果从 \(dp_{i,j,0,0}\) 转移来那么需满足 \(a+b<p-1\),方案数 \(\dfrac{p(p-1)}{2}\)
    • 如果从 \(dp_{i,j,0,1}\) 转移来那么需满足 \(a+b<c-1\),方案数 \(\dfrac{c(c-1)}{2}\)
    • 如果从 \(dp_{i,j,1,0}\) 转移来那么需满足 \(a+b\ge p-1\),方案数 \(\dfrac{p(p+1)}{2}\)
    • 如果从 \(dp_{i,j,1,1}\) 转移来那么需满足 \(p\le a+b+1<p+c\),方案数 \(\dfrac{(p+c)(p+c-1)}{2}-\dfrac{p(p-1)}{2}=\dfrac{c(2n-c+1)}{2}\)
  • \(dp_{i+1,j,1,1}\)
    • 如果从 \(dp_{i,j,0,1}\) 转移来那么需满足 \(a+b+1=c\),方案数 \(c\)
    • 如果从 \(dp_{i,j,1,1}\) 转移来那么需满足 \(a+b+1=p+c\),方案数 \(p-c\)

算下贡献转移一下即可。时间复杂度 \(\mathcal O(\log^2_kA)\)

最好使用滚动数组优化。

const int MAXL=4000;
const int MOD=1e9+7;
int n,alpha,len,m=0,a[MAXL+5],x[MAXL+5];
char A[MAXL+5];int dp[2][MAXL+5][2][2];
int main(){
	scanf("%d%d%s",&n,&alpha,A+1);len=strlen(A+1);
	if(alpha>MAXL) return puts("0"),0;
	for(int i=1;i<=len;i++) a[len-i+1]=A[i]-'0';
	while(len){
		ll cur=0;
		for(int i=len;i;i--){
			cur=cur*10+a[i];a[i]=cur/n;cur%=n;
		} x[++m]=cur;if(!a[len]) len--;
	} int cur=1,pre=0;
	dp[cur][0][0][1]=1;
	for(int i=m;i;i--){
		cur^=pre^=cur^=pre;
		memset(dp[cur],0,sizeof(dp[cur]));
		int c1=1ll*(n+1)*n/2%MOD;
		int c2=1ll*(x[i]+1)*x[i]/2%MOD;
		int c3=1ll*(n-1)*n/2%MOD;
		int c4=1ll*x[i]*(n*2-x[i]-1)/2%MOD;
		int c5=1ll*(x[i]-1)*x[i]/2%MOD;
		int c6=1ll*x[i]*(n*2-x[i]+1)/2%MOD;
		for(int j=0;j<=m-i+1;j++){
			int f0=dp[pre][j][0][0],f1=dp[pre][j][0][1];
			int f2=dp[pre][j][1][0],f3=dp[pre][j][1][1];
			dp[cur][j][0][0]=(1ll*f0*c1+1ll*f1*c2+1ll*f2*c3+1ll*f3*c4)%MOD;
			dp[cur][j][0][1]=(1ll*(x[i]+1)*f1+1ll*(n-x[i]-1)*f3)%MOD;
			dp[cur][j+1][1][0]=(1ll*f0*c3+1ll*f1*c5+1ll*f2*c1+1ll*f3*c6)%MOD;
			dp[cur][j+1][1][1]=(1ll*x[i]*f1+1ll*(n-x[i])*f3)%MOD;
		}
	} int ans=0;
	for(int i=alpha;i<=m;i++){
		ans=(ans+dp[cur][i][0][0])%MOD;
		ans=(ans+dp[cur][i][0][1])%MOD;
	} printf("%d\n",ans);
	return 0;
}
posted @ 2021-04-08 20:37  tzc_wk  阅读(68)  评论(0)    收藏  举报