把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

点此看题面

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

卢卡斯定理

显然有一个基本结论:

\[k|C_i^j\Leftrightarrow C_i^j\equiv0(mod\ k) \]

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

\[C_i^j\equiv C_{i\ div\ k}^{j\ div\ k}\times 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\)(注意,题目里要求\(j\le i\),所以必须先出现\(i>j\)),\(fn=0/1\)表示\(i\)是否小于\(n\)\(fm=0/1\)表示\(j\)是否小于\(m\)

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

代码:\(O(T\log_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\n",DP(Mx,0,0,0));}return 0;//分解为k进制数
}
posted @ 2021-03-27 12:59  TheLostWeak  阅读(87)  评论(0编辑  收藏  举报