【洛谷6669】[清华集训2016] 组合数问题(卢卡斯定理+数位DP)

点此看题面

  • 给定(n,m)和一个质数(k),求有多少(0le ile n,0le jlemin{i,m}),满足(C_i^j)(k)的倍数。
  • 数据组数(le100,n,mle10^{18},kle100)

卢卡斯定理

显然有一个基本结论:

[k|C_i^jLeftrightarrow C_i^jequiv0(mod k) ]

由于(k)是质数,我们可以直接套用卢卡斯定理

[C_i^jequiv C_{i div k}^{j div k} imes C_{i mod k}^{j mod k}(mod k) ]

也就是说,如果我们把(i,j)转化为(k)进制数,只要有一位(i)(j)小就为(0)

所以就可以整一个数位(DP)了。

数位(DP)

我们设(f_{x,p,fn,fm})表示从高往低(DP)到第(x)位,(p=0/1/2)分别表示(i,j)一直相等/出现过(i>j)但没出现过(i<j)/出现过(i>j)然后出现过(i<j)(注意,题目里要求(jle i),所以必须先出现(i>j)),(fn=0/1)表示(i)是否小于(n)(fm=0/1)表示(j)是否小于(m)

转移就是分别枚举它们这一位填什么数即可。

代码:(O(Tlog_kn))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define L 60
#define X 1000000007
using namespace std;
long long N,M;int k,Mx,n[L+5],m[L+5];
int f[L+5][3][2][2];I int DP(CI x,CI p,CI fn,CI fm)//数位DP
{
	if(!x) return p==2;if(~f[x][p][fn][fm]) return f[x][p][fn][fm];//记忆化
	RI i,j,t=0,sn=fn?k-1:n[x],sm=fm?k-1:m[x];for(i=0;i<=sn;++i)
		for(j=0;j<=(p?sm:min(sm,i));++j) t=(t+DP(x-1,p?(p==1?1+(i<j):2):(i>j),fn||i^n[x],fm||j^m[x]))%X;//枚举i,j这位填什么数
	return f[x][p][fn][fm]=t;
}
int main()
{
	RI Tt,i;scanf("%d%d",&Tt,&k);W(Tt--) {memset(f,-1,sizeof(f)),Mx=0,
		scanf("%lld%lld",&N,&M);W(N||M) n[++Mx]=N%k,m[Mx]=M%k,N/=k,M/=k;printf("%d
",DP(Mx,0,0,0));}return 0;//分解为k进制数
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6669.html