题解 [CF582D] Number of Binominal Coefficients

传送门

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

那么考虑 Kummer 定理

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

    cnt=i=1+n+mpii=1+npii=1+mpi=i=1+n+mpinpimpi

    注意 p 进制下这个除法等价于右移
    i,n+mpinpimpi=1 的充要条件是 n,m 右移后做加法时产生了进位,于是得证

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

calc(l,r)=k=lri=0p1j=0p1[i+j=k]=k=lri=0p1[ki[0,p1]]=k=lrmax(min(k,p1)max(k(p1),0),0)=k=lrmin(k,p1)max(k(p1),0)

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

点击查看代码
#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 @   Administrator-09  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示