[蓝桥杯 2019 省 A] 组合数问题-Lucas定理、数位dp

洛谷的题目链接:https://www.luogu.com.cn/problem/P8688


Lucas定理,把\(k|binom{i}{j}\)转换成在k进制下存在某个数位i比j小,再转换成反面计算每一位i都比j大,然后就是经典的数位dp,中间计算的时候可能需要经常求个\(\sum_{i=0}^n \sum_{j=0}^m [i\geq j]\)这样的式子,单独拎出来好写一点x

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int MOD=1e9+7;
const int N=65;
ll f[N],k,d[2][N],g[N][2][2];
ll s(ll n){n%=MOD;return n*(n+1)/2%MOD;}
void add(ll &x,ll y){x+=y;if(x>=y)x%=MOD;}
ll calc(ll n,ll m){return ((s(n+1)-s(max(0ll,n-m)))%MOD+MOD)%MOD;}
void work(ll x,int t){
	rep(i,0,N-1)d[t][i]=0;
	for(int cnt=0;x;x/=k)d[t][++cnt]=x%k;
}
ll dfs(int pos,bool lim0,bool lim1){
	if(pos==0)return 1;
	if(g[pos][lim0][lim1]!=-1)return g[pos][lim0][lim1];
	ll ans=0;
	if(lim0==0&&lim1==0)ans=f[pos];
	else if(lim0==0&&lim1==1)ans=((k-d[1][pos])*dfs(pos-1,0,1)%MOD+(s(d[1][pos])+(k-d[1][pos])*d[1][pos]%MOD)%MOD*f[pos-1]%MOD)%MOD;
	else if(lim0==1&&lim1==0)ans=((d[0][pos]+1)*dfs(pos-1,1,0)%MOD+s(d[0][pos])*f[pos-1]%MOD)%MOD;
	else{
		if(d[0][pos]>=d[1][pos])add(ans,dfs(pos-1,1,1));
		add(ans,dfs(pos-1,1,0)*min(d[1][pos],d[0][pos]+1)%MOD);
		add(ans,dfs(pos-1,0,1)*max(0ll,d[0][pos]-d[1][pos])%MOD);
		add(ans,dfs(pos-1,0,0)*calc(d[0][pos]-1,d[1][pos]-1)%MOD);
	}
	return g[pos][lim0][lim1]=ans;
}
int main(){
	fastio;
	int T;cin>>T>>k;
	f[0]=1;
	rep(i,1,N-1)f[i]=1ll*f[i-1]*s(k)%MOD;
	while(T--){
		ll n,m;
		cin>>n>>m;
		memset(g,-1,sizeof(g));
		work(n,0);work(m,1);
		cout<<((calc(n,m)-dfs(64,1,1))%+MOD+MOD)%MOD<<endl;
	}
	return 0;
}
posted @ 2023-02-09 19:45  yoshinow2001  阅读(31)  评论(0编辑  收藏  举报