「清华集训2016」组合数问题(数位dp)

时间:2020-04-17
本文章向大家介绍「清华集训2016」组合数问题(数位dp),主要包括「清华集训2016」组合数问题(数位dp)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

「清华集训2016」组合数问题(数位dp)

题意:

给定\(n,m,k\) 求:\(\sum_1^n\sum_1^{min(i,m)} [k|C(i,j)]\)

分析:

根据\(\text{lucas}\)定理,\(C(i,j)=C(i\mod k,j\mod k)C(\frac{i}{k},\frac{j}{k})\)

只要出现一个\(i \mod k>j \mod k\)\(\frac{i}{k},\frac{j}{k}\)就会出现

模拟一下这个递归过程,就会发现每次出现的\(i \mod k ,j \mod k\)就是把\(k\)进制下的\(i,j\)依次拆开

所以只要\(k\)进制下\(j\)有一位大于\(i\)即可,数位dp


const int P=1e9+7;

int t,k;
ll A,B;
int a[70],b[70];

ll dp[62][2][2][2][2];

ll dfs(int p,int lim1,int lim2,int lim3,int fl) { 
             // i是否受到限制,j是否受到限制,是否出现了i>j的位置,是否出现了i<j的位置
	if(p==0) return fl;
	if(~dp[p][lim1][lim2][lim3][fl]) return dp[p][lim1][lim2][lim3][fl];
	ll ans=0;
	rep(i,0,lim1?a[p]:k-1) rep(j,0,lim2?b[p]:k-1) {
		if(!lim3 && i<j) continue;
		ans=(ans+dfs(p-1,lim1&&(i==iend),lim2&&(j==jend),lim3||(i>j),fl||(i<j)))%P;
	}
	return dp[p][lim1][lim2][lim3][fl]=ans;
}


ll Solve() {
	int c1=0,c2=0;
	memset(a,0,sizeof a),memset(b,0,sizeof b);
	while(A) a[++c1]=A%k,A/=k;
	while(B) b[++c2]=B%k,B/=k;
	return dfs(max(c1,c2),1,1,0,0);
}

int main(){
	t=rd(),k=rd();
	rep(kase,1,t) {
		A=rd<ll>(),B=rd<ll>();
		memset(dp,-1,sizeof dp);
		printf("%lld\n",Solve());
	}
}


原文地址:https://www.cnblogs.com/chasedeath/p/12721783.html