题解 [CF582D] Number of Binominal Coefficients

传送门

一眼 Lucas 定理,冷静发现 Lucas 定理仅适用于模数是质数的情况

那么考虑 Kummer 定理

  • Kummer 定理:
    \(\binom{n+m}{n}\) 中(注意不是 \(\binom{n}{m}\) 中)含有 \(p\) 的次数是 \(n+m\)\(p\) 进制下的进位次数
    证明:
    含有 \(p\) 的次数是这样一个东西:

    \[\begin{aligned} cnt&=\sum\limits_{i=1}^{+\infty}\lfloor\frac{n+m}{p^i}\rfloor-\sum\limits_{i=1}^{+\infty}\lfloor\frac{n}{p^i}\rfloor-\sum\limits_{i=1}^{+\infty}\lfloor\frac{m}{p^i}\rfloor\\ &=\sum\limits_{i=1}^{+\infty}\lfloor\frac{n+m}{p^i}\rfloor-\lfloor\frac{n}{p^i}\rfloor-\lfloor\frac{m}{p^i}\rfloor\\ \end{aligned} \]

    注意 \(p\) 进制下这个除法等价于右移
    \(\forall i,\lfloor\frac{n+m}{p^i}\rfloor-\lfloor\frac{n}{p^i}\rfloor-\lfloor\frac{m}{p^i}\rfloor=1\) 的充要条件是 \(n, m\) 右移后做加法时产生了进位,于是得证

回到本题
那么容易想到数位 DP
我一开始莫名其妙假在了上面“注意不是”的那个地方
\(f_{i, j, 0/1, 0/1}\) 为高到低第 \(i\) 位,产生了 \(j\) 次进位,当前位是否产生进位,\(n+m\) 是否卡上界的合法方案数
转移大力分类讨论,需要实现一个 \(\operatorname{calc}(l, r)\)\(n, m\in[0, p-1], n+m\in[l, r]\) 的方案数
这个东西我只会大力分类讨论值域段
然后得到了某篇题解的一个启发,可以这样推式子处理:

\[\begin{aligned} \operatorname{calc}(l, r)&=\sum\limits_{k=l}^r\sum\limits_{i=0}^{p-1}\sum\limits_{j=0}^{p-1}[i+j=k]\\ &=\sum\limits_{k=l}^r\sum\limits_{i=0}^{p-1}[k-i\in[0, p-1]]\\ &=\sum\limits_{k=l}^r \max(\min(k, p-1)-max(k-(p-1), 0), 0)\\ &=\sum\limits_{k=l}^r \min(k, p-1)-max(k-(p-1), 0) \end{aligned}\]

于是只需要分两段简单讨论即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 3510
#define pb push_back
#define ll long long
//#define int long long

int p, a;
char t[N];
vector<int> s;
int f[N][N][2][2];
const ll mod=1e9+7, inv2=(mod+1)>>1;

vector<int> tran(vector<int> s) {
	int now=0;
	vector<int> tem[2], ans;
	for (tem[now]=s; tem[now].size(); now^=1) {
		ll rest=0;
		bool any=0;
		tem[now^1].clear();
		for (auto it:tem[now]) {
			rest=rest*10+it;
			if (rest>=p) tem[now^1].pb(rest/p), rest%=p, any=1;
			else if (any) tem[now^1].pb(0);
		}
		ans.pb(rest);
	}
	if (!ans.size()) ans.pb(0);
	while (ans.size()>1&&!ans.back()) ans.pop_back();
	// cout<<"ans: "; for (int i=ans.size()-1; ~i; --i) cout<<ans[i]<<' '; cout<<endl;
	return ans;
}

// ll calc(ll l, ll r) {
// 	l=max(l, 0ll);
// 	r=min(r, 2ll*(p-1));
// 	if (l>r) return 0;
// 	cout<<"calc: "<<l<<' '<<r<<endl;
// 	ll ans=0;
// 	for (int i=0; i<p; ++i)
// 		for (int j=0; j<p; ++j)
// 			if (l<=i+j && i+j<=r) ++ans;
// 	cout<<"return: "<<ans<<endl;
// 	return ans;
// }

inline ll qsum(ll n) {return n*(n+1)%mod*inv2%mod;}

ll calc(ll l, ll r) {
	l=max(l, 0ll);
	r=min(r, 2ll*(p-1));
	if (l>r) return 0;
	// cout<<"calc: "<<l<<' '<<r<<endl;
	ll ans=0;
	if (r>=p-1) {
		ll tl=max((ll)p-1, l), tr=r;
		ans=(ans+(tr-tl+1)*2*(p-1)-(qsum(tr)-qsum(tl-1)))%mod;
	}
	if (l<p-1) {
		ll tl=l, tr=min((ll)p-2, r);
		ans=(ans+qsum(tr)-qsum(tl-1))%mod;
	}
	ans=(ans+(r-l+1))%mod;
	// cout<<"return: "<<ans<<endl;
	return ans;
}

ll dfs(int i, int j, bool over, bool lim) {
	if (i<0) return !over&&j>=a;
	if (~f[i][j][over][lim]) return f[i][j][over][lim];
	ll ans=0;
	if (over) {
		if (lim) {
			ans=(ans+calc(p+s[i], p+s[i])*dfs(i-1, j, 0, 1))%mod;
			ans=(ans+calc(p, p+s[i]-1)*dfs(i-1, j, 0, 0))%mod;
			ans=(ans+calc(p+s[i]-1, p+s[i]-1)*dfs(i-1, j+1, 1, 1))%mod;
			ans=(ans+calc(p-1, p+s[i]-2)*dfs(i-1, j+1, 1, 0))%mod;
		}
		else {
			ans=(ans+calc(p, 2*(p-1))*dfs(i-1, j, 0, 0))%mod;
			ans=(ans+calc(p-1, 2*(p-1))*dfs(i-1, j+1, 1, 0))%mod;
		}
	}
	else {
		if (lim) {
			ans=(ans+calc(s[i], s[i])*dfs(i-1, j, 0, 1))%mod;
			ans=(ans+calc(0, s[i]-1)*dfs(i-1, j, 0, 0))%mod;
			ans=(ans+calc(s[i]-1, s[i]-1)*dfs(i-1, j+1, 1, 1))%mod;
			ans=(ans+calc(0, s[i]-2)*dfs(i-1, j+1, 1, 0))%mod;
		}
		else {
			ans=(ans+calc(0, p-1)*dfs(i-1, j, 0, 0))%mod;
			ans=(ans+calc(0, p-2)*dfs(i-1, j+1, 1, 0))%mod;
		}
	}
	return f[i][j][over][lim]=ans;
}

signed main()
{
	// cout<<double(sizeof(f))/1000/1000<<endl;
	scanf("%d%d%s", &p, &a, t);
	s.resize(strlen(t));
	for (int i=0; i<s.size(); ++i) s[i]=t[i]-'0';
	s=tran(s);
	// cout<<s.size()<<endl; exit(0);
	memset(f, -1, sizeof(f));
	printf("%lld\n", dfs(s.size()-1, 0, 0, 1));
	
	return 0;
}
posted @ 2022-04-26 20:12  Administrator-09  阅读(20)  评论(0)    收藏  举报